Skip to content

Commit dbedcf2

Browse files
srtaalejzimeg
andauthored
feat(experiment): add Slack brand theme for charm/huh prompts (#351)
Co-authored-by: Eden Zimbelman <eden.zimbelman@salesforce.com>
1 parent 4f51fa6 commit dbedcf2

2 files changed

Lines changed: 125 additions & 5 deletions

File tree

internal/iostreams/charm.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func charmInputPrompt(_ *IOStreams, _ context.Context, message string, cfg Input
3333
if cfg.Required {
3434
field.Validate(huh.ValidateMinLength(1))
3535
}
36-
err := huh.NewForm(huh.NewGroup(field)).Run()
36+
err := huh.NewForm(huh.NewGroup(field)).WithTheme(ThemeSlack()).Run()
3737
if err != nil {
3838
return "", err
3939
}
@@ -46,7 +46,7 @@ func charmConfirmPrompt(_ *IOStreams, _ context.Context, message string, default
4646
field := huh.NewConfirm().
4747
Title(message).
4848
Value(&choice)
49-
err := huh.NewForm(huh.NewGroup(field)).Run()
49+
err := huh.NewForm(huh.NewGroup(field)).WithTheme(ThemeSlack()).Run()
5050
if err != nil {
5151
return false, err
5252
}
@@ -76,7 +76,7 @@ func charmSelectPrompt(_ *IOStreams, _ context.Context, msg string, options []st
7676
field.Height(cfg.PageSize + 2)
7777
}
7878

79-
err := huh.NewForm(huh.NewGroup(field)).Run()
79+
err := huh.NewForm(huh.NewGroup(field)).WithTheme(ThemeSlack()).Run()
8080
if err != nil {
8181
return SelectPromptResponse{}, err
8282
}
@@ -95,7 +95,7 @@ func charmPasswordPrompt(_ *IOStreams, _ context.Context, message string, cfg Pa
9595
if cfg.Required {
9696
field.Validate(huh.ValidateMinLength(1))
9797
}
98-
err := huh.NewForm(huh.NewGroup(field)).Run()
98+
err := huh.NewForm(huh.NewGroup(field)).WithTheme(ThemeSlack()).Run()
9999
if err != nil {
100100
return PasswordPromptResponse{}, err
101101
}
@@ -115,7 +115,7 @@ func charmMultiSelectPrompt(_ *IOStreams, _ context.Context, message string, opt
115115
Options(opts...).
116116
Value(&selected)
117117

118-
err := huh.NewForm(huh.NewGroup(field)).Run()
118+
err := huh.NewForm(huh.NewGroup(field)).WithTheme(ThemeSlack()).Run()
119119
if err != nil {
120120
return []string{}, err
121121
}

internal/iostreams/charm_theme.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2022-2026 Salesforce, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package iostreams
16+
17+
// Slack brand theme for charmbracelet/huh prompts.
18+
// Uses official Slack brand colors to give the CLI a fun, playful feel.
19+
20+
import (
21+
"github.com/charmbracelet/huh"
22+
"github.com/charmbracelet/lipgloss"
23+
)
24+
25+
// Slack brand colors according to https://a.slack-edge.com/4d5bb/marketing/img/media-kit/slack_brand_guidelines_september2020.pdf
26+
var (
27+
slackAubergine = lipgloss.Color("#7C2852")
28+
slackBlue = lipgloss.Color("#36c5f0")
29+
slackGreen = lipgloss.Color("#2eb67d")
30+
slackYellow = lipgloss.Color("#ecb22e")
31+
slackRed = lipgloss.Color("#e01e5a")
32+
slackPool = lipgloss.Color("#78d7dd")
33+
slackLegalGray = lipgloss.Color("#5e5d60")
34+
slackOptionText = lipgloss.AdaptiveColor{Light: "#1d1c1d", Dark: "#f4ede4"}
35+
slackDescriptionText = lipgloss.AdaptiveColor{Light: "#454447", Dark: "#b9b5b0"}
36+
slackPlaceholderText = lipgloss.AdaptiveColor{Light: "#5e5d60", Dark: "#868380"}
37+
)
38+
39+
// ThemeSlack returns a huh theme styled with Slack brand colors.
40+
func ThemeSlack() *huh.Theme {
41+
t := huh.ThemeBase()
42+
43+
// Focused styles apply to the field the user is currently interacting with.
44+
// Blurred styles apply to visible fields that are not currently active.
45+
t.Focused.Base = t.Focused.Base.
46+
BorderForeground(slackAubergine)
47+
t.Focused.Title = lipgloss.NewStyle().
48+
Foreground(slackAubergine).
49+
Bold(true)
50+
t.Focused.Description = lipgloss.NewStyle().
51+
Foreground(slackDescriptionText)
52+
t.Focused.ErrorIndicator = lipgloss.NewStyle().
53+
Foreground(slackRed).
54+
SetString(" *")
55+
t.Focused.ErrorMessage = lipgloss.NewStyle().
56+
Foreground(slackRed)
57+
58+
// Select styles
59+
t.Focused.SelectSelector = lipgloss.NewStyle().
60+
Foreground(slackBlue).
61+
SetString("❱ ")
62+
t.Focused.Option = lipgloss.NewStyle().
63+
Foreground(slackOptionText)
64+
t.Focused.NextIndicator = lipgloss.NewStyle().
65+
Foreground(slackPool).
66+
MarginLeft(1).
67+
SetString("↓")
68+
t.Focused.PrevIndicator = lipgloss.NewStyle().
69+
Foreground(slackPool).
70+
MarginRight(1).
71+
SetString("↑")
72+
73+
// Multi-select styles
74+
t.Focused.MultiSelectSelector = lipgloss.NewStyle().
75+
Foreground(slackYellow).
76+
SetString("❱ ")
77+
t.Focused.SelectedOption = lipgloss.NewStyle().
78+
Foreground(slackGreen)
79+
t.Focused.SelectedPrefix = lipgloss.NewStyle().
80+
Foreground(slackGreen).
81+
SetString("[✓] ")
82+
t.Focused.UnselectedOption = lipgloss.NewStyle().
83+
Foreground(slackOptionText)
84+
t.Focused.UnselectedPrefix = lipgloss.NewStyle().
85+
Foreground(slackLegalGray).
86+
SetString("[ ] ")
87+
88+
// Text input styles
89+
t.Focused.TextInput.Cursor = lipgloss.NewStyle().
90+
Foreground(slackYellow)
91+
t.Focused.TextInput.Prompt = lipgloss.NewStyle().
92+
Foreground(slackBlue)
93+
t.Focused.TextInput.Placeholder = lipgloss.NewStyle().
94+
Foreground(slackPlaceholderText)
95+
t.Focused.TextInput.Text = lipgloss.NewStyle().
96+
Foreground(slackOptionText)
97+
98+
// Button styles
99+
button := lipgloss.NewStyle().
100+
Padding(0, 2).
101+
MarginRight(1)
102+
t.Focused.FocusedButton = button.
103+
Foreground(lipgloss.Color("#ffffff")).
104+
Background(slackAubergine).
105+
Bold(true)
106+
t.Focused.BlurredButton = button.
107+
Foreground(slackLegalGray).
108+
Background(lipgloss.Color("#f8f8f8"))
109+
110+
// Blurred field styles — subdued version of focused
111+
t.Blurred = t.Focused
112+
t.Blurred.Base = t.Focused.Base.
113+
BorderStyle(lipgloss.HiddenBorder())
114+
t.Blurred.SelectSelector = lipgloss.NewStyle().SetString(" ")
115+
t.Blurred.MultiSelectSelector = lipgloss.NewStyle().SetString(" ")
116+
t.Blurred.NextIndicator = lipgloss.NewStyle()
117+
t.Blurred.PrevIndicator = lipgloss.NewStyle()
118+
119+
return t
120+
}

0 commit comments

Comments
 (0)