Skip to content

Commit 89aaa26

Browse files
authored
👌 Improve dropdown title bar (#192)
This PR makes three visible changes: 1. The "default" behaviour of the right chevron is to go from right-facing (closed) to down-facing (open), instead of down-facing (closed) to up-facing (open). There is also a rotate transition on opening/closing. The old default behaviour can be retained by using the new `:chevron: down-up` directive option. 2. The prefix icon (optional), title text, and chevron state icon are now all better aligned 3. The top/bottom padding is now 0.5em instead of 1em The PR also introduces three new CSS variables to control font sizes of the dropdown: ```css --sd-fontsize-tabs-label: 1rem; --sd-fontsize-dropdown-title: 1rem; --sd-fontweight-dropdown-title: 700; ``` --- Internally, the HTML / CSS is changed, such that the title is now an `inline-flex` box, with three columns arranged with `justify-content: space-between`: | icon (optional) | text (`flex-grow: 1`) | state chevron | | -------------- | -------------------- | -------------- | Also, the state chevron was previously two distinct SVGs (with one hidden), but now is one that get rotated on open/close.
1 parent 169c09d commit 89aaa26

12 files changed

+171
-109
lines changed

docs/_static/furo.css

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
body {
2+
--sd-fontsize-dropdown: var(--admonition-font-size);
3+
--sd-fontsize-dropdown-title: var(--admonition-title-font-size);
4+
--sd-fontweight-dropdown-title: 500;
5+
}

docs/conf.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
}
4747
if html_theme == "furo":
4848
html_css_files = [
49-
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
49+
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css",
50+
"furo.css",
5051
]
5152
html_theme_options = {
5253
"sidebar_hide_name": True,

docs/css_variables.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ The defaults are:
6363
--sd-color-tabs-underline-inactive: transparent;
6464
--sd-color-tabs-overline: rgb(222, 222, 222);
6565
--sd-color-tabs-underline: rgb(222, 222, 222);
66-
--sd-fontsize-tabs-label: 1rem
66+
--sd-fontsize-tabs-label: 1rem;
67+
--sd-fontsize-dropdown-title: 1rem;
68+
--sd-fontweight-dropdown-title: 700;
6769
}
6870
```

docs/dropdowns.md

+36-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Dropdown content
3434
````
3535
`````
3636

37-
## Dropdown opening animations
37+
## Opening animations
3838

3939
Use `:animate: fade-in` or `:animate: fade-in-slide-down` options to animate the reveal of the hidden content.
4040

@@ -50,10 +50,41 @@ Use `:animate: fade-in` or `:animate: fade-in-slide-down` options to animate the
5050
{{ loremipsum }}
5151
:::
5252

53+
## More examples
54+
55+
:::{dropdown} Dropdown with icon
56+
:icon: quote
57+
58+
Dropdown content
59+
:::
60+
61+
:::{dropdown} Dropdown with icon and very long title, *lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec auctor, nunc nec fermentum ultricies, nunc sapien ultricies nunc, nec ultricies sapien sapien nec sapien*
62+
:icon: quote
63+
64+
Dropdown content
65+
:::
66+
67+
:::{dropdown} Using option `:chevron: down-up`
68+
:chevron: down-up
69+
70+
Dropdown content
71+
:::
72+
5373
## Dropdowns in other components
5474

5575
Dropdowns can be nested inside other components, such as inside parent dropdowns or within [grid items](./grids.md).
5676

77+
::::{admonition} Here is an admonition with a dropdown
78+
79+
Admonition content
80+
81+
:::{dropdown} Dropdown inside admonition
82+
:icon: quote
83+
84+
{{ loremipsum }}
85+
:::
86+
::::
87+
5788
::::{dropdown} Parent dropdown title
5889
:open:
5990

@@ -94,6 +125,10 @@ color
94125
icon
95126
: Set an [octicon icon](icons) to prefix the dropdown header.
96127

128+
chevron
129+
: The open-close direction of the chevron.
130+
One of: `right-down`, `down-up`.
131+
97132
animate
98133
: Animate the dropdown opening (`fade-in` or `fade-in-slide-down`).
99134

sphinx_design/compiled/style.min.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sphinx_design/dropdown.py

+30-26
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ class DropdownDirective(SphinxDirective):
7676
"open": directives.flag, # make open by default
7777
"color": make_choice(SEMANTIC_COLORS),
7878
"icon": make_choice(list_octicons()),
79+
"chevron": make_choice(
80+
["right-down", "down-up"]
81+
), # chevron direction closed-open
7982
"animate": make_choice(("fade-in", "fade-in-slide-down")),
8083
"margin": margin_option,
8184
"name": directives.unchanged,
@@ -114,6 +117,7 @@ def run(self):
114117
type="dropdown",
115118
has_title=len(self.arguments) > 0,
116119
icon=self.options.get("icon"),
120+
chevron=self.options.get("chevron"),
117121
**classes,
118122
)
119123
self.set_source_info(container)
@@ -152,27 +156,18 @@ def run(self):
152156
# TODO option to not have card css (but requires more formatting)
153157
use_card = True
154158

155-
open_marker = create_component(
156-
"dropdown-open-marker",
157-
classes=["sd-summary-up"],
158-
children=[
159-
nodes.raw(
160-
"",
161-
nodes.Text(get_octicon("chevron-up", height="1.5em")),
162-
format="html",
163-
)
164-
],
159+
marker_type = (
160+
"chevron-down" if node["chevron"] == "down-up" else "chevron-right"
165161
)
166-
closed_marker = create_component(
167-
"dropdown-closed-marker",
168-
classes=["sd-summary-down"],
169-
children=[
170-
nodes.raw(
171-
"",
172-
nodes.Text(get_octicon("chevron-down", height="1.5em")),
173-
format="html",
174-
)
175-
],
162+
state_marker = nodes.inline(
163+
"",
164+
"",
165+
nodes.raw(
166+
"",
167+
nodes.Text(get_octicon(marker_type, height="1.5em")),
168+
format="html",
169+
),
170+
classes=["sd-summary-state-marker", f"sd-summary-{marker_type}"],
176171
)
177172

178173
newnode = dropdown_main(
@@ -183,25 +178,36 @@ def run(self):
183178
)
184179

185180
if node["has_title"]:
186-
title_children = node[0].children
181+
title_text_children = node[0].children
187182
if node[0].get("ids"):
188183
newnode["ids"] += node[0]["ids"]
189184
body_children = node[1:]
190185
else:
191-
title_children = [
186+
title_text_children = [
192187
nodes.raw(
193188
"...",
194-
nodes.Text(get_octicon("kebab-horizontal", height="1.5em")),
189+
nodes.Text(
190+
get_octicon(
191+
"kebab-horizontal", height="1.5em", classes=["no-title"]
192+
)
193+
),
195194
format="html",
196195
)
197196
]
198197
body_children = node.children
198+
title_text_node = nodes.inline(
199+
"",
200+
"",
201+
*title_text_children,
202+
classes=["sd-summary-text"],
203+
)
204+
title_children = [title_text_node, state_marker]
199205
if node["icon"]:
200206
title_children.insert(
201207
0,
202208
nodes.raw(
203209
"",
204-
nodes.Text(get_octicon(node["icon"], height="1em")),
210+
get_octicon(node["icon"], height="1em"),
205211
classes=["sd-summary-icon"],
206212
format="html",
207213
),
@@ -211,8 +217,6 @@ def run(self):
211217
"",
212218
"",
213219
*title_children,
214-
closed_marker,
215-
open_marker,
216220
classes=["sd-summary-title"]
217221
+ (["sd-card-header"] if use_card else [])
218222
+ node["title_classes"],

style/_dropdown.scss

+66-51
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
details.sd-dropdown {
22

33
position: relative;
4-
5-
.sd-summary-title {
6-
font-weight: 700;
7-
// don't overlap the chevron
8-
padding-right: 3em !important;
9-
-moz-user-select: none;
10-
-ms-user-select: none;
11-
-webkit-user-select: none;
12-
user-select: none;
13-
}
4+
font-size: var(--sd-fontsize-dropdown);
145

156
&:hover {
167
cursor: pointer;
@@ -20,64 +11,88 @@ details.sd-dropdown {
2011
cursor: default;
2112
}
2213

23-
summary {
14+
summary.sd-summary-title {
15+
padding: 0.5em 1em;
16+
font-size: var(--sd-fontsize-dropdown-title);
17+
font-weight: var(--sd-fontweight-dropdown-title);
18+
19+
// make the text un-selectable
20+
user-select: none;
21+
-moz-user-select: none;
22+
-ms-user-select: none;
23+
-webkit-user-select: none;
24+
2425
// hide the default triangle marker
2526
list-style: none;
26-
padding: 1em;
27+
// chrome doesn't yet support list-style
28+
&::-webkit-details-marker {
29+
display: none;
30+
}
2731

28-
// Ellipsis added when no title
29-
.sd-octicon.no-title {
30-
vertical-align: middle;
32+
&:focus {
33+
outline: none;
3134
}
32-
}
3335

34-
&[open] summary .sd-octicon.no-title {
35-
visibility: hidden;
36-
}
36+
// The title is split into three parts:
37+
// 1. The icon (optional), which should be on the left
38+
// 2. The text, which should be in the middle, and take all available space
39+
// 3. The state marker, which should be on the right
3740

38-
// chrome doesn't yet support list-style
39-
summary::-webkit-details-marker {
40-
display: none;
41-
}
41+
display: inline-flex;
42+
justify-content: space-between;
4243

43-
summary:focus {
44-
outline: none;
45-
}
44+
.sd-summary-icon {
45+
margin-right: 0.6em;
46+
// align the icon vertically within its container
47+
display: inline-flex;
48+
align-items: center;
49+
}
4650

47-
.sd-summary-icon {
48-
margin-right: 0.5em;
49-
}
51+
.sd-summary-icon svg {
52+
opacity: 0.8;
53+
}
5054

51-
.sd-summary-icon svg {
52-
opacity: 0.8;
53-
}
55+
.sd-summary-text {
56+
flex-grow: 1;
57+
line-height: 1.5;
58+
// note, we add right padding to the text, rather than a left margin to the state marker,
59+
// because when you rotate the state marker, left is no longer left
60+
padding-right: 0.5rem;
61+
}
5462

55-
summary:hover .sd-summary-up svg,
56-
summary:hover .sd-summary-down svg {
57-
opacity: 1;
58-
transform: scale(1.1);
59-
}
63+
.sd-summary-state-marker {
64+
pointer-events: none;
65+
// align the icon vertically within its container
66+
display: inline-flex;
67+
align-items: center;
68+
}
6069

61-
.sd-summary-up svg,
62-
.sd-summary-down svg {
63-
display: block;
64-
opacity: 0.6;
65-
}
70+
// make the state marker a bit more prominent when hovered
71+
.sd-summary-state-marker svg {
72+
opacity: 0.6;
73+
}
74+
&:hover .sd-summary-state-marker svg {
75+
opacity: 1;
76+
transform: scale(1.1);
77+
}
6678

67-
.sd-summary-up,
68-
.sd-summary-down {
69-
pointer-events: none;
70-
position: absolute;
71-
right: 1em;
72-
top: 1em;
7379
}
7480

75-
&[open] > .sd-summary-title .sd-summary-down {
81+
// Hide the octicon added as a placeholder for when no title is provided,
82+
// but only when the summary is open
83+
&[open] summary .sd-octicon.no-title {
7684
visibility: hidden;
7785
}
7886

79-
&:not([open]) > .sd-summary-title .sd-summary-up {
80-
visibility: hidden;
87+
// setup how state marker changes on open/close of the dropdown
88+
.sd-summary-chevron-right {
89+
transition: 0.25s;
90+
}
91+
&[open] > .sd-summary-title .sd-summary-chevron-right {
92+
transform: rotate(90deg);
93+
}
94+
&[open] > .sd-summary-title .sd-summary-chevron-down {
95+
transform: rotate(180deg);
8196
}
8297

8398
// Hide the card body border when not open

style/_variables.scss

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
@each $color, $value in $semantic-colors {
88
--sd-color-#{$color}-highlight: #{mix(black, $value, 15%)};
99
}
10+
// semantic colors for backgrounds
11+
@each $color, $value in $semantic-colors {
12+
--sd-color-#{$color}-bg: #{rgba($value, 0.2)};
13+
}
1014
// colors for text on top of a semantic color background
1115
@each $color, $value in $semantic-colors {
1216
--sd-color-#{$color}-text: #{text-color($value)};
@@ -30,4 +34,8 @@
3034
--sd-color-tabs-overline: rgb(222, 222, 222);
3135
--sd-color-tabs-underline: rgb(222, 222, 222);
3236
--sd-fontsize-tabs-label: 1rem;
37+
// dropdown
38+
--sd-fontsize-dropdown: inherit;
39+
--sd-fontsize-dropdown-title: 1rem;
40+
--sd-fontweight-dropdown-title: 700;
3341
}

0 commit comments

Comments
 (0)