Skip to content

Commit c64cbfd

Browse files
committed
chore: clean up documentation and comments, remove CSS fallback values
1 parent 93dd849 commit c64cbfd

7 files changed

Lines changed: 58 additions & 126 deletions

File tree

static/css/v3/carousel-buttons.css

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
22
* Carousel buttons – single block with prev/next controls.
3-
* Wrapper uses Surface-Strong background; inner buttons are borderless, icon turns blue on hover.
43
*/
54

65
.carousel-buttons {
@@ -12,7 +11,7 @@
1211
width: 44px;
1312
height: 24px;
1413
border-radius: 8px;
15-
background: var(--color-surface-strong, #EFEFF1);
14+
background: var(--color-surface-strong);
1615
}
1716

1817
.carousel-buttons .btn-carousel {
@@ -36,11 +35,11 @@
3635
}
3736

3837
.carousel-buttons .btn-carousel:hover:not(:disabled) {
39-
color: var(--color-secondary-dark-blue, #0077B8);
38+
color: var(--color-secondary-dark-blue);
4039
}
4140

4241
.carousel-buttons .btn-carousel[data-hover]:not(:disabled) {
43-
color: var(--color-secondary-dark-blue, #0077B8);
42+
color: var(--color-secondary-dark-blue);
4443
}
4544

4645
.carousel-buttons .btn-carousel:disabled,

static/css/v3/content-modal.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,7 @@
158158
.content-modal__prose h5,
159159
.content-modal__prose h6 { font-size: var(--font-size-small); }
160160

161-
/* The first child of the prose body should sit flush with the top —
162-
skip the heading/paragraph top margin in that one case. */
161+
/* Sit flush with the top — no leading margin on the first prose element. */
163162
.content-modal__prose > :first-child {
164163
margin-top: 0;
165164
}

static/css/v3/dialog.css

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,8 @@
3939
display: flex !important;
4040
}
4141

42-
/* Lock background page scroll while any dialog is open. CSS-only via :has(),
43-
so it works without JavaScript. Covers both hash-based dialogs
44-
(.dialog-modal:target) and checkbox-based ones (library filter).
45-
Relies on `scrollbar-gutter: stable` in foundations.css so removing the
46-
scrollbar here doesn't reflow the page horizontally. */
47-
html:has(.dialog-modal:target),
48-
html:has(.library-filter__toggle:checked) {
42+
/* Lock background page scroll while any dialog is open. */
43+
html:has(.dialog-modal:target) {
4944
overflow: hidden;
5045
}
5146

@@ -68,7 +63,7 @@ html:has(.library-filter__toggle:checked) {
6863
z-index: 1;
6964
display: flex;
7065
flex-direction: column;
71-
gap: var(--space-large, 16px);
66+
gap: var(--space-large);
7267
width: 695px;
7368
max-width: calc(100vw - 2 * var(--space-large));
7469
background-color: var(--color-surface-page);

static/css/v3/foundations.css

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,14 @@ body.v3 * {
1919
scrollbar-width: thin;
2020
}
2121

22-
/* Reserve the scrollbar gutter permanently so any state that toggles the
23-
scrollbar (e.g. dialog scroll-lock in dialog.css) doesn't reflow the
24-
page horizontally. */
25-
html {
22+
/* Reserve the scrollbar gutter so dialog scroll-lock doesn't reflow the page. */
23+
html.v3 {
2624
scrollbar-gutter: stable;
2725
}
2826

29-
/* Third-party scroll-lock libraries (e.g. Boost Gecko search widget) add
30-
inline padding-right to <body> to compensate for a disappearing
31-
scrollbar. The scrollbar-gutter rule above already reserves that space,
32-
so the third-party compensation is double-padding and shifts the page.
33-
Force body padding-right to zero to neutralise it. */
34-
body {
27+
/* Neutralise third-party scroll-lock libs (e.g. Boost Gecko search) that
28+
add inline body padding-right — our gutter above already reserves it. */
29+
body.v3 {
3530
padding-right: 0 !important;
3631
}
3732

static/css/v3/testimonial-card.css

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,13 @@
55
overflow: hidden;
66
}
77

8-
.testimonial-card__carousel {
9-
display: flex;
10-
padding: 0 var(--space-large);
11-
flex-direction: column;
12-
align-items: flex-start;
13-
gap: var(--space-large);
14-
align-self: stretch;
15-
}
16-
178
.testimonial-card__text-wrapper {
189
display: flex;
1910
padding: var(--space-xl);
2011
border-radius: var(--space-xl);
2112
background: var(--color-surface-mid);
2213
}
2314

24-
/* Quote bubble becomes a link when the testimonial has a slug — clicking
25-
anywhere in the bubble opens the matching Content Modal. */
2615
.testimonial-card__quote-link {
2716
display: block;
2817
color: inherit;
@@ -40,9 +29,8 @@
4029
fill: var(--color-surface-strong);
4130
}
4231

43-
/* Suppress the default outline on the link itself — it stretches the full
44-
slide width and would bleed into the neighboring slide. Render the focus
45-
ring on the bubble with a negative offset so it stays inside the bubble. */
32+
/* Default link outline would bleed into the neighboring slide; render the
33+
focus ring inside the bubble instead (see rule below). */
4634
.testimonial-card__quote-link:focus-visible {
4735
outline: none;
4836
}
@@ -60,7 +48,7 @@
6048
line-height: var(--line-height-tight);
6149
letter-spacing: var(--letter-spacing-tight);
6250

63-
/* Cap at 3 lines and truncate with an ellipsis on overflow. */
51+
/* 3-line clamp with ellipsis. */
6452
display: -webkit-box;
6553
-webkit-box-orient: vertical;
6654
-webkit-line-clamp: 3;
@@ -95,14 +83,11 @@
9583
align-items: flex-start;
9684
gap: var(--space-s);
9785

98-
99-
/* Sans/Desktop/Regular/XS/Default */
10086
font-family: var(--font-sans);
10187
font-size: var(--font-size-xs);
10288
font-style: normal;
10389
font-weight: 400;
10490
line-height: 120%;
105-
/* 14.4px */
10691
letter-spacing: -0.12px;
10792
}
10893

@@ -152,10 +137,10 @@
152137
display: flex;
153138
flex-direction: column;
154139

155-
/* Hide off-screen slides from the tab order and accessibility tree.
156-
Delayed transition keeps the outgoing slide visible during the
157-
0.3s carousel slide animation. The active slide overrides this
158-
in the inline <style> block emitted per-testimonial. */
140+
/* Hide off-screen slides from tab order. The 0.3s delay matches the
141+
slide animation so the outgoing slide stays visible until it has
142+
fully translated out. Active slide overrides this in the inline
143+
<style> block emitted per-testimonial. */
159144
visibility: hidden;
160145
transition: visibility 0s linear 0.3s;
161146
}
@@ -172,12 +157,11 @@
172157
}
173158

174159
/* ============================================
175-
CSS-only carousel state — driven by hidden radios.
176-
The radios are at the start of .testimonial-card; sibling selectors
177-
translate the track and toggle nav/dot active states accordingly.
160+
CSS-only carousel state — hidden radios at the top of .testimonial-card
161+
drive everything via sibling :checked selectors.
178162
============================================ */
179163

180-
/* Hide the radios visually but keep them keyboard-accessible. */
164+
/* Visually hidden but keyboard-accessible. */
181165
.testimonial-card__radio {
182166
position: absolute;
183167
width: 1px;
@@ -190,34 +174,23 @@
190174
border: 0;
191175
}
192176

193-
/* All per-state rules (track translation, active nav block, active dot,
194-
active CTA, focus ring) are emitted by the Django template via an inline
195-
<style> block in `_testimonial_card.html`, so they scale with the actual
196-
testimonial count instead of being hardcoded. */
177+
/* Per-state rules (track translation, active nav/dot/CTA, focus ring) are
178+
emitted by the Django template's inline <style> in `_testimonial_card.html`
179+
so they scale with the testimonial count. */
197180

198-
/* Per-state prev/next nav blocks are hidden by default; the inline <style>
199-
block reveals only the one matching the active radio. */
200181
.testimonial-card__nav {
201182
display: none;
202183
}
203184

204-
/* The carousel-buttons labels need cursor:pointer because <label> isn't
205-
a button by default. */
206185
.testimonial-card__nav .btn-carousel {
207186
cursor: pointer;
208187
}
209188

210-
/* ============================================
211-
PAGINATION DOTS
212-
Absolutely positioned to the right of the card so they sit on the
213-
same visual row as the user profile (which is at the bottom-left of
214-
each slide). Lifted off the card's bottom edge to clear the divider
215-
and the Read-more CTA below.
216-
============================================ */
189+
/* Pagination dots — sit on the same visual row as the user profile,
190+
above the divider and Read-more CTA. */
217191
.testimonial-card__dots {
218192
position: absolute;
219-
/* Card padding-bottom + CTA height (~36px) + small gap + hr-area
220-
spacing. Tweak if the button height or surrounding gaps change. */
193+
/* Card padding + CTA height (~36px) + gap. Re-tune if button size changes. */
221194
bottom: calc(3 * var(--space-large) + 36px);
222195
right: var(--space-large);
223196
display: flex;
@@ -240,14 +213,7 @@
240213
background-color: var(--color-text-secondary);
241214
}
242215

243-
/* The active dot's `opacity: 1` highlight is emitted by the inline <style>
244-
in the template (per-state rules). */
245-
246-
/* ============================================
247-
READ-MORE CTA — full-width primary button, in flow.
248-
Hidden by default; the inline <style> in the template shows only the
249-
one matching the active radio.
250-
============================================ */
216+
/* Read-more CTA — full-width primary button. */
251217
.testimonial-card__cta {
252218
display: none;
253219
width: calc(100% - 2 * var(--space-large));

templates/v3/includes/_carousel_buttons.html

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
{% comment %}
2+
Carousel Buttons — prev/next control pair.
3+
Renders <button>s for JS-driven carousels or <a>s for no-JS :target/anchor flows.
4+
25
Variables:
3-
carousel_id (optional, for JS): wraps with id="{carousel_id}-controls"
4-
prev_label, next_label (optional): aria-labels (defaults: "Previous", "Next")
5-
as_links (optional, default False): when True, renders <a href> controls so the
6-
buttons work without JS (e.g. for :target-driven modals).
7-
prev_url, next_url (used when as_links=True): destination URLs.
8-
Pass an empty string ("") to render the disabled visual for an endpoint
9-
(e.g. prev_url="" on the first item, next_url="" on the last).
6+
carousel_id (string, optional, default unset) — when set, wraps controls in <div id="{carousel_id}-controls"> for JS targeting
7+
prev_label (string, optional, default "Previous") — aria-label for the prev control
8+
next_label (string, optional, default "Next") — aria-label for the next control
9+
as_links (bool, optional, default false) — when true, renders <a href> controls (e.g. for :target-driven modals)
10+
prev_url (string, optional, default "") — destination when as_links=true; "" renders the disabled visual (endpoint)
11+
next_url (string, optional, default "") — destination when as_links=true; "" renders the disabled visual (endpoint)
12+
13+
Usage (JS-driven carousel):
14+
{% include "v3/includes/_carousel_buttons.html" with carousel_id="my-carousel" %}
1015

11-
Usage (JS-driven carousel): {% include "v3/includes/_carousel_buttons.html" with carousel_id="my-carousel" %}
12-
Usage (no-JS, anchor-based): {% include "v3/includes/_carousel_buttons.html" with as_links=True prev_url="#item-1" next_url="#item-3" %}
16+
Usage (no-JS, anchor-based):
17+
{% include "v3/includes/_carousel_buttons.html" with as_links=True prev_url="#item-1" next_url="#item-3" %}
1318
{% endcomment %}
1419
<div class="carousel-buttons" {% if carousel_id %}id="{{ carousel_id }}-controls"{% endif %} role="group" aria-label="Carousel navigation">
1520
{% if as_links %}

templates/v3/includes/_testimonial_card.html

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,58 +21,42 @@
2121

2222
{% with carousel_id='_testimonials_cards_carousel' total=testimonials|length %}
2323
{% comment %}
24-
Per-state rules generated from the testimonials loop so the rule count
25-
scales with the actual data. Each radio's id is unique, so we target it
26-
directly instead of using :nth-of-type. Covers: track translation,
27-
per-state prev/next nav visibility, active dot highlight, per-state CTA
28-
visibility, and focus ring on the active nav button.
24+
Per-state rules emitted from the testimonials loop so the rule count
25+
scales with the data. Each radio's id is unique, so we target it directly
26+
rather than using :nth-of-type.
2927
{% endcomment %}
3028
<style>
3129
{% for testimonial in testimonials %}
32-
/* When this slide's radio is the :checked one, shift the slide track
33-
horizontally so this slide lands in view. forloop.counter0 maps
34-
slide 1 -> 0%, slide 2 -> -100%, slide 3 -> -200%, and so on. */
30+
/* Shift the track to bring this slide into view (-N00% per zero-indexed slide). */
3531
#{{ carousel_id }}-{{ forloop.counter }}:checked ~ .cards-carousel__track .testimonial-card__list {
3632
transform: translateX(-{{ forloop.counter0 }}00%);
3733
}
38-
/* When this slide is active, reveal its matching prev/next nav block
39-
inside the card header. All nav blocks are display:none by default
40-
(see .testimonial-card__nav in testimonial-card.css). */
34+
/* Reveal this slide's nav block (others are display:none in testimonial-card.css). */
4135
#{{ carousel_id }}-{{ forloop.counter }}:checked ~ .card__header .testimonial-card__nav--{{ forloop.counter }} {
4236
display: inline-flex;
4337
}
44-
/* When this slide is active, bring the matching pagination dot to
45-
full opacity. Inactive dots remain at the base dimmed opacity */
38+
/* Highlight the active dot; inactive dots stay dimmed via the base rule. */
4639
#{{ carousel_id }}-{{ forloop.counter }}:checked ~ .testimonial-card__dots .testimonial-card__dot:nth-child({{ forloop.counter }}) {
4740
opacity: 1;
4841
}
49-
/* When this slide is active, show its matching Read-more CTA. All
50-
CTAs are display:none by default (see .testimonial-card__cta). */
42+
/* Show this slide's CTA (others are display:none). */
5143
#{{ carousel_id }}-{{ forloop.counter }}:checked ~ .testimonial-card__cta--{{ forloop.counter }} {
5244
display: flex;
5345
}
54-
/* When this slide is active, restore visibility on its list-item so
55-
its quote-link and user-profile links re-enter the keyboard tab
56-
order. Inactive slides stay visibility:hidden via the base rule. */
46+
/* Restore the active slide's visibility so its tab targets re-enter the tab order. */
5747
#{{ carousel_id }}-{{ forloop.counter }}:checked ~ .cards-carousel__track .testimonial-card__list .testimonial-card__list-item:nth-child({{ forloop.counter }}) {
5848
visibility: visible;
5949
transition-delay: 0s;
6050
}
61-
/* The radios are visually hidden, so when the user keyboard-focuses
62-
one (Tab + arrow keys), surface a visible focus ring on the
63-
matching prev/next nav wrapper in the card header. */
51+
/* Mirror the hidden radio's focus onto the visible nav wrapper. */
6452
#{{ carousel_id }}-{{ forloop.counter }}:focus-visible ~ .card__header .testimonial-card__nav--{{ forloop.counter }} {
6553
outline: 2px solid var(--color-stroke-link-accent);
6654
}
6755
{% endfor %}
6856
</style>
6957

7058
<section class="card testimonial-card" id="{{ carousel_id }}" role="region" aria-labelledby="{{ carousel_id }}-heading">
71-
{% comment %}
72-
Hidden radios drive carousel state without JS. Sibling selectors on
73-
`.testimonial-card__radio:nth-of-type(N):checked` translate the track,
74-
highlight the active dot, and reveal the matching prev/next nav block.
75-
{% endcomment %}
59+
{% comment %} Hidden radios drive all carousel state via the per-state rules above. {% endcomment %}
7660
{% for testimonial in testimonials %}
7761
<input type="radio"
7862
name="{{ carousel_id }}-state"
@@ -86,10 +70,7 @@
8670
<div class="card__header">
8771
<h2 id="{{ carousel_id }}-heading" class="card__title">{{ heading }}</h2>
8872

89-
{% comment %}
90-
Per-state prev/next: one block per testimonial; CSS shows only the
91-
block matching the currently-checked radio. Wraps cyclically.
92-
{% endcomment %}
73+
{% comment %} One nav block per slide; prev/next wrap cyclically (first ↔ last). {% endcomment %}
9374
{% for testimonial in testimonials %}
9475
<div class="carousel-buttons testimonial-card__nav testimonial-card__nav--{{ forloop.counter }}" role="group" aria-label="Carousel navigation">
9576
{% if forloop.first %}
@@ -110,7 +91,7 @@ <h2 id="{{ carousel_id }}-heading" class="card__title">{{ heading }}</h2>
11091
{% endfor %}
11192
</div>
11293

113-
<hr class="card__hr" aria-hidden="True" />
94+
<hr class="card__hr" aria-hidden="true" />
11495

11596
{% if total > 1 %}
11697
<div class="testimonial-card__dots" aria-label="Testimonial pagination">
@@ -146,13 +127,9 @@ <h2 id="{{ carousel_id }}-heading" class="card__title">{{ heading }}</h2>
146127
</section>
147128
</div>
148129

149-
<hr class="card__hr" aria-hidden="True" />
130+
<hr class="card__hr" aria-hidden="true" />
150131

151-
{% comment %}
152-
Per-state "Read more" CTA: one button per testimonial, sits below the
153-
divider at the bottom of the card. CSS shows only the one matching the
154-
active radio. Targets the same modal slug as the quote-bubble link.
155-
{% endcomment %}
132+
{% comment %} One CTA per slide; targets the same modal slug as the quote-bubble link. {% endcomment %}
156133
{% for testimonial in testimonials %}
157134
{% if testimonial.slug %}
158135
<a href="#{{ testimonial.slug }}"
@@ -164,11 +141,7 @@ <h2 id="{{ carousel_id }}-heading" class="card__title">{{ heading }}</h2>
164141
{% endfor %}
165142
</section>
166143

167-
{% comment %}
168-
Bundle the matching Content Modals as a sibling of the card. Each
169-
testimonial that carries `content` gets a modal rendered here,
170-
so clicking its quote bubble opens the modal via :target.
171-
{% endcomment %}
144+
{% comment %} Modals rendered as siblings of the card so :target wires up the quote-bubble links. {% endcomment %}
172145
{% if testimonials.0.content %}
173146
{% include "v3/includes/_content_modal_group.html" with items=testimonials only %}
174147
{% endif %}

0 commit comments

Comments
 (0)