@neuravision/construct 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +5 -3
- package/aperture.demo.html +21 -0
- package/components/README.md +42 -1
- package/components/alert.css +4 -1
- package/components/aperture.css +441 -0
- package/components/card.css +6 -0
- package/components/drawer.css +3 -0
- package/components/field.css +3 -0
- package/components/index.css +3 -0
- package/components/modal.css +7 -1
- package/docs/aperture-review.md +55 -0
- package/docs/aperture.md +114 -0
- package/docs/assets/aperture-review/after-desktop.png +0 -0
- package/docs/assets/aperture-review/after-mobile.png +0 -0
- package/docs/assets/aperture-review/after-monochrome.png +0 -0
- package/docs/assets/aperture-review/before-desktop.png +0 -0
- package/examples/aperture/showcase.css +363 -0
- package/examples/aperture/showcase.js +145 -0
- package/package.json +4 -1
- package/schemas/components.schema.json +19 -1
- package/schemas/semantic.schema.json +18 -2
- package/scripts/build-tokens.mjs +20 -0
- package/standalone/aperture.css +3 -0
- package/tokens/components.json +13 -0
- package/tokens/primitives.json +14 -0
- package/tokens/semantic.dark.json +9 -0
- package/tokens/semantic.high-contrast-dark.json +9 -0
- package/tokens/semantic.high-contrast.json +9 -0
- package/tokens/semantic.light.json +10 -0
- package/tokens/tokens.css +150 -0
- package/tokens/tokens.js +166 -0
- package/tokens/tokens.json +166 -0
- package/tokens/tokens.ts +166 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,53 @@ All notable changes to `@neuravision/construct` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.3.1] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Grid content slots clamp their content track. `ct-card__body`, `ct-modal__body`,
|
|
13
|
+
`ct-drawer__body`, `ct-field`, `ct-alert` and `ct-confirmation` sized their column to the
|
|
14
|
+
widest item's max-content size, so a table, a long identifier or any wide embedded content
|
|
15
|
+
stretched the surface past the width it was given instead of wrapping or scrolling inside it.
|
|
16
|
+
A scroll container one level down — the usual shape when a framework component host sits
|
|
17
|
+
between the slot and `ct-table-wrap` — was stretched with it and stopped scrolling, which
|
|
18
|
+
pushed the overflow out to the page or the application shell. Regression coverage measures
|
|
19
|
+
every slot's track against its own content box at 320px.
|
|
20
|
+
- Documented that `ct-table-wrap` needs `tabindex="0"`, `role="region"` and an accessible name:
|
|
21
|
+
now that wide tables scroll inside the wrapper again, keyboard users must be able to reach
|
|
22
|
+
the columns that are out of view.
|
|
23
|
+
|
|
24
|
+
## [2.3.0] - 2026-09-16
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Aperture: an additional opt-in signature look with a split oval, proportional offset,
|
|
29
|
+
cobalt surfaces and oversized grotesk typography. Reusable marks, wordmarks, posters,
|
|
30
|
+
offset heading lines, split rules, actions, panels, native radio choices and disclosures ship through the full component
|
|
31
|
+
bundle and the standalone `components/aperture.css` entry. Datum Grammar remains available.
|
|
32
|
+
- Cobalt and graphite primitives, Aperture semantic/component tokens, and contrast gates
|
|
33
|
+
for all four built-in theme scopes. The look includes reduced-motion, forced-colors,
|
|
34
|
+
keyboard-focus and touch-target support.
|
|
35
|
+
- A standalone interactive preview, integration guide, visual polish review, and eleven
|
|
36
|
+
Storybook examples covering the complete look, compact layout, themes, primitives,
|
|
37
|
+
monochrome composition, long action labels, Docs link isolation and right-to-left layout.
|
|
38
|
+
- Aperture's seam and offset carry through shared action hit areas, heading lines,
|
|
39
|
+
composition rules and staggered specimens, with a larger closing wordmark.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Storybook Docs prose link styles no longer override links inside embedded story
|
|
44
|
+
previews. Aperture action labels retain their theme colors instead of becoming
|
|
45
|
+
dark text on dark buttons with orange underlines. Regression coverage checks
|
|
46
|
+
primary, quiet and wordmark links at rest, on hover and with keyboard focus in
|
|
47
|
+
all four theme scopes.
|
|
48
|
+
- Aperture action labels retain their contrast on hover and active links. Logical
|
|
49
|
+
action padding preserves the seam in RTL; disclosure arrows and keyboard focus
|
|
50
|
+
follow the signature's direction and palette.
|
|
51
|
+
- Package export validation accepts Windows CRLF shebangs and checks POSIX executable
|
|
52
|
+
permission bits only on platforms that expose them.
|
|
53
|
+
- Updated the locked development dependencies `nanoid` and `postcss` to patched versions.
|
|
54
|
+
|
|
8
55
|
## [2.2.1] - 2026-07-28
|
|
9
56
|
|
|
10
57
|
### Fixed
|
package/README.md
CHANGED
|
@@ -25,11 +25,12 @@
|
|
|
25
25
|
Construct is a **single source of design truth** that ships portable CSS, validated design tokens, and opt-in headless behaviors — without framework lock-in. Style values live once as JSON tokens and compile to CSS custom properties, JSON, and typed TypeScript exports. Components use `ct-` classes and ARIA/data state, so they work anywhere that renders DOM.
|
|
26
26
|
|
|
27
27
|
- 🎯 **Three token layers** — primitive, semantic, and component contracts with schema and contrast validation
|
|
28
|
-
- 🧩 **Framework-agnostic** —
|
|
28
|
+
- 🧩 **Framework-agnostic** — 51 standalone component style entries and a full CSS bundle
|
|
29
29
|
- ♿ **Accessibility first** — WCAG 2.2 AA targets, tested ARIA/keyboard controllers, focus and forced-colors contracts
|
|
30
30
|
- ⚙️ **Optional behavior** — nine typed, dependency-free ESM controllers; importing CSS never starts JavaScript
|
|
31
|
-
- 🎨 **
|
|
31
|
+
- 🎨 **Four theme scopes** — `light`, `dark`, `high-contrast`, `high-contrast-dark`, with system-preference fallback
|
|
32
32
|
- 📐 **The "datum" grammar** — a signature orange reference line for focus / active / current state
|
|
33
|
+
- ◐ **Aperture** — an additional signature look with a split oval, cobalt surfaces and expressive typography; [usage and preview](docs/aperture.md)
|
|
33
34
|
- 🛠️ **Typed tokens** — autocomplete-friendly TypeScript exports
|
|
34
35
|
- 📖 **Interactive docs** — every component documented & a11y-tested in [Storybook](https://samyssmile.github.io/construct/)
|
|
35
36
|
|
|
@@ -133,7 +134,7 @@ npx construct-theme --theme node_modules/@neuravision/construct/themes/walnut.th
|
|
|
133
134
|
|
|
134
135
|
## 🧩 Components
|
|
135
136
|
|
|
136
|
-
|
|
137
|
+
51 standalone component style entries, documented and accessibility-tested in [Storybook](https://samyssmile.github.io/construct/).
|
|
137
138
|
|
|
138
139
|
| Category | Components |
|
|
139
140
|
|----------|-----------|
|
|
@@ -216,6 +217,7 @@ npm test # run behavior, Storybook, touch & forced-colors browse
|
|
|
216
217
|
|
|
217
218
|
- [Design Guidelines](docs/guidelines.md) — do's, don'ts & accessibility patterns
|
|
218
219
|
- [Component Usage](components/README.md) — HTML patterns and examples
|
|
220
|
+
- [Aperture](docs/aperture.md) — split-oval signature, integration and theme examples
|
|
219
221
|
- [Token Reference](tokens/README.md) — token structure & naming
|
|
220
222
|
- [Architecture](docs/architecture.md) — package layers and ownership boundaries
|
|
221
223
|
- [Headless Behaviors](docs/behaviors.md) — typed controllers and lifecycle
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="de" data-theme="light">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="theme-color" content="#F5F5F0">
|
|
7
|
+
<meta name="description" content="Aperture – ein zusätzlicher Signature Look für Construct. Kobaltblau, große Typografie und ein versetztes, geteiltes Oval.">
|
|
8
|
+
<title>Aperture — Construct Signature Look</title>
|
|
9
|
+
<link rel="stylesheet" href="./foundations.css">
|
|
10
|
+
<link rel="stylesheet" href="./standalone/aperture.css">
|
|
11
|
+
<link rel="stylesheet" href="./examples/aperture/showcase.css">
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="aperture-preview"></div>
|
|
15
|
+
<noscript>Diese interaktive Vorschau benötigt JavaScript. Die Aperture-Komponenten selbst bestehen aus HTML und CSS.</noscript>
|
|
16
|
+
<script type="module">
|
|
17
|
+
import { createApertureShowcase } from './examples/aperture/showcase.js';
|
|
18
|
+
document.querySelector('#aperture-preview').replaceWith(createApertureShowcase({ themeControls: true }));
|
|
19
|
+
</script>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
package/components/README.md
CHANGED
|
@@ -214,6 +214,11 @@ Content card with header, body, and footer:
|
|
|
214
214
|
</section>
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
+
`ct-card__body` clamps its content track, as every grid content slot does (`ct-modal__body`,
|
|
218
|
+
`ct-drawer__body`, `ct-field`, `ct-alert`, `ct-confirmation`). Content wider than the card wraps,
|
|
219
|
+
truncates, or scrolls inside its own scroll container — `ct-table-wrap` for tables — instead of
|
|
220
|
+
stretching the card past the width it was given.
|
|
221
|
+
|
|
217
222
|
### Frame
|
|
218
223
|
|
|
219
224
|
Owned surface for figures, code samples, media, and callouts. The outer frame owns its border,
|
|
@@ -235,6 +240,38 @@ content is a figure, use the `figure` itself as `ct-frame__content` so its `figc
|
|
|
235
240
|
valid direct child. Customize the owned surface with `--ct-frame-background`, `--ct-frame-border`,
|
|
236
241
|
`--ct-frame-border-width`, `--ct-frame-radius`, and `--ct-frame-shadow`.
|
|
237
242
|
|
|
243
|
+
### Aperture Signature Look
|
|
244
|
+
|
|
245
|
+
An additional opt-in grammar: split ovals, a proportional offset, cobalt and paper,
|
|
246
|
+
and oversized grotesk typography. Import `@neuravision/construct/components/aperture.css`
|
|
247
|
+
or use the full component bundle. Wrap the composition in `.ct-aperture`.
|
|
248
|
+
|
|
249
|
+
```html
|
|
250
|
+
<section class="ct-aperture">
|
|
251
|
+
<h2 class="ct-aperture-heading">Room for character.</h2>
|
|
252
|
+
<figure class="ct-aperture-poster">
|
|
253
|
+
<div class="ct-aperture-poster__art">
|
|
254
|
+
<span class="ct-aperture-mark" aria-hidden="true"></span>
|
|
255
|
+
</div>
|
|
256
|
+
<figcaption class="ct-aperture-poster__title">Open by design.</figcaption>
|
|
257
|
+
</figure>
|
|
258
|
+
</section>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Primitives: `ct-aperture-mark`, `ct-aperture-wordmark`, `ct-aperture-label`,
|
|
262
|
+
`ct-aperture-display`, `ct-aperture-heading`, `ct-aperture-line`,
|
|
263
|
+
decorative `ct-aperture-rule`, `ct-aperture-poster`,
|
|
264
|
+
`ct-aperture-action` (and `--quiet`), `ct-aperture-panel`, native radio
|
|
265
|
+
`ct-aperture-choices` / `ct-aperture-choice`, and native `ct-aperture-disclosure`.
|
|
266
|
+
|
|
267
|
+
Action text and its displaced arrow disc remain inside one native link or button;
|
|
268
|
+
the visual seam is part of the same hit area. Put `ct-aperture-line` on spans inside
|
|
269
|
+
one heading to offset later lines, and hide decorative rules with `aria-hidden="true"`.
|
|
270
|
+
|
|
271
|
+
All colors use `--component-aperture-*` tokens. Mark size can be overridden with
|
|
272
|
+
`--ct-aperture-mark-size`. See [the design and integration guide](../docs/aperture.md)
|
|
273
|
+
and **Patterns → Aperture Grammar** in Storybook.
|
|
274
|
+
|
|
238
275
|
### Datum Primitives
|
|
239
276
|
|
|
240
277
|
The reference-line grammar from `components/datum.css` — Construct's orange signature as reusable
|
|
@@ -274,7 +311,7 @@ One datum per surface — see `docs/guidelines.md`.
|
|
|
274
311
|
Basic data table with variants:
|
|
275
312
|
|
|
276
313
|
```html
|
|
277
|
-
<div class="ct-table-wrap">
|
|
314
|
+
<div class="ct-table-wrap" tabindex="0" role="region" aria-label="Projects">
|
|
278
315
|
<table class="ct-table ct-table--striped">
|
|
279
316
|
<thead>
|
|
280
317
|
<tr>
|
|
@@ -299,6 +336,10 @@ Basic data table with variants:
|
|
|
299
336
|
</div>
|
|
300
337
|
```
|
|
301
338
|
|
|
339
|
+
`ct-table-wrap` scrolls a table that is wider than its container. A scrollable region needs a
|
|
340
|
+
keyboard tab stop and its own accessible name, so give the wrapper `tabindex="0"`, `role="region"`
|
|
341
|
+
and an `aria-label` — otherwise keyboard users cannot reach the columns that are out of view.
|
|
342
|
+
|
|
302
343
|
Variants:
|
|
303
344
|
- `ct-table--striped` - Alternating row backgrounds
|
|
304
345
|
- `ct-table--compact` - Reduced padding
|
package/components/alert.css
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
--ct-alert-accent: var(--color-brand-primary);
|
|
5
5
|
|
|
6
6
|
display: grid;
|
|
7
|
-
|
|
7
|
+
/* The content column is clamped so long words or wide embedded content
|
|
8
|
+
stay inside the alert instead of stretching it. */
|
|
9
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
8
10
|
gap: var(--space-4);
|
|
9
11
|
padding: var(--space-5) var(--space-6);
|
|
10
12
|
border-radius: var(--radius-md);
|
|
@@ -24,6 +26,7 @@
|
|
|
24
26
|
|
|
25
27
|
.ct-alert__content {
|
|
26
28
|
display: grid;
|
|
29
|
+
grid-template-columns: minmax(0, 1fr);
|
|
27
30
|
gap: var(--space-2);
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/* Aperture: a split oval, a deliberate offset, generous negative space.
|
|
2
|
+
Opt in with .ct-aperture. No global theme or Datum overrides.
|
|
3
|
+
Marks are decorative: render them with aria-hidden="true". */
|
|
4
|
+
.ct-aperture {
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
background: var(--component-aperture-paper);
|
|
7
|
+
color: var(--component-aperture-ink);
|
|
8
|
+
font-family: var(--component-aperture-font-family);
|
|
9
|
+
font-size: var(--font-size-md);
|
|
10
|
+
line-height: 1.5;
|
|
11
|
+
accent-color: var(--component-aperture-ink);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.ct-aperture,
|
|
15
|
+
.ct-aperture *,
|
|
16
|
+
.ct-aperture *::before,
|
|
17
|
+
.ct-aperture *::after {
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ct-aperture :is(h1, h2, h3, p, figure) {
|
|
22
|
+
margin: 0;
|
|
23
|
+
font-family: inherit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.ct-aperture :is(a, button, summary, input, [tabindex]):focus-visible,
|
|
27
|
+
.ct-aperture-action:focus-visible,
|
|
28
|
+
.ct-aperture-choice:has(input:focus-visible) {
|
|
29
|
+
outline: 3px solid currentColor;
|
|
30
|
+
outline-offset: 5px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ct-aperture ::selection {
|
|
34
|
+
color: var(--component-aperture-paper);
|
|
35
|
+
background: var(--component-aperture-ink);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* The same two half-ovals work at wordmark, control, and poster scale.
|
|
39
|
+
Percentages keep the seam and vertical offset proportional at every size. */
|
|
40
|
+
.ct-aperture-mark {
|
|
41
|
+
position: relative;
|
|
42
|
+
display: inline-block;
|
|
43
|
+
flex: none;
|
|
44
|
+
inline-size: var(--ct-aperture-mark-size, var(--component-aperture-mark-size));
|
|
45
|
+
aspect-ratio: 1;
|
|
46
|
+
color: inherit;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ct-aperture-mark::before,
|
|
50
|
+
.ct-aperture-mark::after {
|
|
51
|
+
content: '';
|
|
52
|
+
position: absolute;
|
|
53
|
+
inline-size: 46%;
|
|
54
|
+
block-size: 84%;
|
|
55
|
+
background: currentColor;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.ct-aperture-mark::before {
|
|
59
|
+
inset-block-start: 0;
|
|
60
|
+
inset-inline-start: 0;
|
|
61
|
+
border-start-start-radius: 100% 50%;
|
|
62
|
+
border-end-start-radius: 100% 50%;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.ct-aperture-mark::after {
|
|
66
|
+
inset-block-end: 0;
|
|
67
|
+
inset-inline-end: 0;
|
|
68
|
+
border-start-end-radius: 100% 50%;
|
|
69
|
+
border-end-end-radius: 100% 50%;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.ct-aperture-wordmark {
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: var(--space-4);
|
|
76
|
+
color: inherit;
|
|
77
|
+
font-family: var(--component-aperture-font-family);
|
|
78
|
+
font-size: var(--font-size-2xl);
|
|
79
|
+
font-weight: var(--font-weight-bold);
|
|
80
|
+
letter-spacing: -0.065em;
|
|
81
|
+
line-height: 1;
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
min-block-size: var(--component-aperture-action-height);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ct-aperture-wordmark:is(:hover, :active) {
|
|
87
|
+
color: inherit;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ct-aperture-label {
|
|
91
|
+
display: block;
|
|
92
|
+
font-size: var(--font-size-xs);
|
|
93
|
+
font-weight: var(--font-weight-bold);
|
|
94
|
+
letter-spacing: 0.08em;
|
|
95
|
+
line-height: 1.5;
|
|
96
|
+
text-transform: uppercase;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.ct-aperture .ct-aperture-display {
|
|
100
|
+
font-size: clamp(3.25rem, 1rem + 9.6cqi, 10.5rem);
|
|
101
|
+
font-weight: var(--font-weight-bold);
|
|
102
|
+
letter-spacing: -0.075em;
|
|
103
|
+
line-height: 0.91;
|
|
104
|
+
overflow-wrap: anywhere;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ct-aperture .ct-aperture-heading {
|
|
108
|
+
font-size: clamp(2.25rem, 1.5rem + 3.5cqi, 5rem);
|
|
109
|
+
font-weight: var(--font-weight-bold);
|
|
110
|
+
letter-spacing: -0.065em;
|
|
111
|
+
line-height: 1.02;
|
|
112
|
+
overflow-wrap: anywhere;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* A second line steps out of the first line's axis, like the two halves
|
|
116
|
+
of the mark. Keep the text in normal flow so zoom and wrapping still work. */
|
|
117
|
+
.ct-aperture-line {
|
|
118
|
+
display: block;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.ct-aperture-line + .ct-aperture-line {
|
|
122
|
+
margin-inline-start: 0.24em;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* One interrupted rule, with the same 46 / 8 / 46 proportions as the mark.
|
|
126
|
+
Decorative only: render with aria-hidden="true". */
|
|
127
|
+
.ct-aperture-rule {
|
|
128
|
+
display: grid;
|
|
129
|
+
grid-template-columns: 1fr 1fr;
|
|
130
|
+
column-gap: 8%;
|
|
131
|
+
padding-block: 1px 7px;
|
|
132
|
+
color: var(--component-aperture-border);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.ct-aperture-rule::before,
|
|
136
|
+
.ct-aperture-rule::after {
|
|
137
|
+
content: '';
|
|
138
|
+
border-block-start: 1px solid currentColor;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.ct-aperture-rule::after {
|
|
142
|
+
transform: translateY(6px);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* The poster is a figure, never a cropped interactive surface. */
|
|
146
|
+
.ct-aperture-poster {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
justify-content: space-between;
|
|
150
|
+
gap: var(--space-8);
|
|
151
|
+
min-inline-size: 0;
|
|
152
|
+
padding: clamp(1.5rem, 3cqi, 3rem);
|
|
153
|
+
color: var(--component-aperture-on-accent);
|
|
154
|
+
background: var(--component-aperture-accent);
|
|
155
|
+
border-radius: var(--component-aperture-panel-radius);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.ct-aperture-poster__art {
|
|
159
|
+
display: grid;
|
|
160
|
+
place-items: center;
|
|
161
|
+
flex: 1;
|
|
162
|
+
padding-block: var(--space-7);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.ct-aperture-poster__art .ct-aperture-mark {
|
|
166
|
+
--ct-aperture-mark-size: 82%;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.ct-aperture-poster__caption {
|
|
170
|
+
display: flex;
|
|
171
|
+
flex-wrap: wrap;
|
|
172
|
+
align-items: end;
|
|
173
|
+
justify-content: space-between;
|
|
174
|
+
gap: var(--space-5);
|
|
175
|
+
font-size: var(--font-size-xs);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.ct-aperture-poster__title {
|
|
179
|
+
font-size: clamp(2rem, 4cqi, 4rem);
|
|
180
|
+
font-weight: var(--font-weight-bold);
|
|
181
|
+
letter-spacing: -0.075em;
|
|
182
|
+
line-height: 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* The label and disc share one hit area. The open seam and lowered disc
|
|
186
|
+
carry the mark's offset into actions without splitting keyboard focus. */
|
|
187
|
+
.ct-aperture-action {
|
|
188
|
+
position: relative;
|
|
189
|
+
isolation: isolate;
|
|
190
|
+
display: inline-flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: space-between;
|
|
193
|
+
gap: var(--space-6);
|
|
194
|
+
min-block-size: calc(var(--component-aperture-action-height) + 8px);
|
|
195
|
+
max-inline-size: 100%;
|
|
196
|
+
padding-block: 0 8px;
|
|
197
|
+
padding-inline: var(--space-6) 0;
|
|
198
|
+
border: 0;
|
|
199
|
+
border-radius: 0;
|
|
200
|
+
background: transparent;
|
|
201
|
+
color: var(--component-aperture-paper);
|
|
202
|
+
font-family: var(--component-aperture-font-family);
|
|
203
|
+
font-size: var(--font-size-sm);
|
|
204
|
+
font-weight: var(--font-weight-bold);
|
|
205
|
+
line-height: 1.4;
|
|
206
|
+
text-align: start;
|
|
207
|
+
text-decoration: none;
|
|
208
|
+
overflow-wrap: anywhere;
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.ct-aperture-action:is(:hover, :active) {
|
|
213
|
+
color: var(--component-aperture-paper);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.ct-aperture-action::before {
|
|
217
|
+
content: '';
|
|
218
|
+
position: absolute;
|
|
219
|
+
z-index: -1;
|
|
220
|
+
inset-block: 0 8px;
|
|
221
|
+
inset-inline: 0 calc(var(--component-aperture-action-height) + 6px);
|
|
222
|
+
border: 1px solid var(--component-aperture-ink);
|
|
223
|
+
border-radius: var(--radius-round);
|
|
224
|
+
background: var(--component-aperture-ink);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.ct-aperture-action__icon {
|
|
228
|
+
display: grid;
|
|
229
|
+
place-items: center;
|
|
230
|
+
flex: none;
|
|
231
|
+
inline-size: var(--component-aperture-action-height);
|
|
232
|
+
block-size: var(--component-aperture-action-height);
|
|
233
|
+
border: 1px solid var(--component-aperture-ink);
|
|
234
|
+
border-radius: 50%;
|
|
235
|
+
background: var(--component-aperture-ink);
|
|
236
|
+
color: var(--component-aperture-paper);
|
|
237
|
+
font-size: var(--font-size-xl);
|
|
238
|
+
font-weight: var(--font-weight-regular);
|
|
239
|
+
transform: translateY(8px);
|
|
240
|
+
transition: transform var(--duration-normal) var(--easing-standard);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.ct-aperture-action__icon svg {
|
|
244
|
+
inline-size: 20px;
|
|
245
|
+
block-size: 20px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.ct-aperture-action--quiet,
|
|
249
|
+
.ct-aperture-action--quiet:is(:hover, :active) {
|
|
250
|
+
color: var(--component-aperture-ink);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.ct-aperture-action--quiet::before,
|
|
254
|
+
.ct-aperture-action--quiet .ct-aperture-action__icon {
|
|
255
|
+
background: transparent;
|
|
256
|
+
color: var(--component-aperture-ink);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.ct-aperture-action:focus-visible {
|
|
260
|
+
outline-color: var(--component-aperture-ink);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.ct-aperture-action:disabled {
|
|
264
|
+
cursor: not-allowed;
|
|
265
|
+
opacity: var(--opacity-disabled);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@media (hover: hover) {
|
|
269
|
+
.ct-aperture-action:where(:not(:disabled)):hover .ct-aperture-action__icon {
|
|
270
|
+
transform: translateY(0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.ct-aperture-action:where(:not(:disabled)):hover {
|
|
274
|
+
text-decoration: underline;
|
|
275
|
+
text-underline-offset: 0.2em;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* Native radio inputs retain keyboard behavior. The text, inset border and
|
|
280
|
+
weight identify selection without depending on the accent color. */
|
|
281
|
+
.ct-aperture-choices {
|
|
282
|
+
display: flex;
|
|
283
|
+
flex-wrap: wrap;
|
|
284
|
+
gap: var(--space-2);
|
|
285
|
+
margin: 0;
|
|
286
|
+
padding: 0;
|
|
287
|
+
border: 0;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.ct-aperture-choice {
|
|
291
|
+
position: relative;
|
|
292
|
+
display: inline-flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
min-inline-size: 44px;
|
|
296
|
+
min-block-size: 44px;
|
|
297
|
+
padding: var(--space-3) var(--space-5);
|
|
298
|
+
border: 1px solid transparent;
|
|
299
|
+
border-radius: var(--radius-round);
|
|
300
|
+
font: inherit;
|
|
301
|
+
font-size: var(--font-size-xs);
|
|
302
|
+
cursor: pointer;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.ct-aperture-choice input,
|
|
306
|
+
.ct-aperture-choices legend {
|
|
307
|
+
position: absolute;
|
|
308
|
+
inline-size: 1px;
|
|
309
|
+
block-size: 1px;
|
|
310
|
+
padding: 0;
|
|
311
|
+
overflow: hidden;
|
|
312
|
+
clip-path: inset(50%);
|
|
313
|
+
white-space: nowrap;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.ct-aperture-choice:has(input:checked) {
|
|
317
|
+
border-color: currentColor;
|
|
318
|
+
font-weight: var(--font-weight-bold);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.ct-aperture-panel {
|
|
322
|
+
min-inline-size: 0;
|
|
323
|
+
padding: clamp(1.5rem, 3cqi, 3rem);
|
|
324
|
+
border: 1px solid var(--component-aperture-border);
|
|
325
|
+
border-radius: var(--component-aperture-panel-radius);
|
|
326
|
+
background: var(--component-aperture-surface);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.ct-aperture-disclosure {
|
|
330
|
+
border-block-start: 1px solid var(--component-aperture-border);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.ct-aperture-disclosure:last-child {
|
|
334
|
+
border-block-end: 1px solid var(--component-aperture-border);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.ct-aperture-disclosure summary {
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: space-between;
|
|
341
|
+
gap: var(--space-6);
|
|
342
|
+
min-block-size: var(--component-aperture-action-height);
|
|
343
|
+
padding-block: var(--space-6);
|
|
344
|
+
list-style: none;
|
|
345
|
+
font-size: var(--font-size-lg);
|
|
346
|
+
font-weight: var(--font-weight-bold);
|
|
347
|
+
cursor: pointer;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.ct-aperture-disclosure summary::-webkit-details-marker {
|
|
351
|
+
display: none;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.ct-aperture-disclosure summary::after {
|
|
355
|
+
content: '';
|
|
356
|
+
flex: none;
|
|
357
|
+
inline-size: 10px;
|
|
358
|
+
block-size: 10px;
|
|
359
|
+
margin-inline-end: var(--space-3);
|
|
360
|
+
border-inline-end: 2px solid currentColor;
|
|
361
|
+
border-block-end: 2px solid currentColor;
|
|
362
|
+
transform: rotate(45deg);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.ct-aperture-disclosure[open] summary::after {
|
|
366
|
+
transform: rotate(225deg);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.ct-aperture-disclosure summary:dir(rtl)::after {
|
|
370
|
+
transform: rotate(-45deg);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.ct-aperture-disclosure[open] summary:dir(rtl)::after {
|
|
374
|
+
transform: rotate(135deg);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.ct-aperture-disclosure > :not(summary) {
|
|
378
|
+
max-inline-size: 60ch;
|
|
379
|
+
padding-block-end: var(--space-7);
|
|
380
|
+
color: var(--component-aperture-muted);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@media (prefers-reduced-motion: reduce) {
|
|
384
|
+
.ct-aperture-action__icon {
|
|
385
|
+
transition: none;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.ct-aperture-action:hover .ct-aperture-action__icon {
|
|
389
|
+
transform: translateY(8px);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
@media (forced-colors: active) {
|
|
394
|
+
.ct-aperture {
|
|
395
|
+
background: Canvas;
|
|
396
|
+
color: CanvasText;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.ct-aperture-mark::before,
|
|
400
|
+
.ct-aperture-mark::after {
|
|
401
|
+
background: CanvasText;
|
|
402
|
+
forced-color-adjust: none;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.ct-aperture-poster,
|
|
406
|
+
.ct-aperture-panel {
|
|
407
|
+
background: Canvas;
|
|
408
|
+
color: CanvasText;
|
|
409
|
+
border: 1px solid CanvasText;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.ct-aperture-action,
|
|
413
|
+
.ct-aperture-action:is(:hover, :active),
|
|
414
|
+
.ct-aperture-action::before,
|
|
415
|
+
.ct-aperture-action__icon {
|
|
416
|
+
border-color: ButtonText;
|
|
417
|
+
background: ButtonFace;
|
|
418
|
+
color: ButtonText;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.ct-aperture-rule {
|
|
422
|
+
color: CanvasText;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.ct-aperture-action:focus-visible {
|
|
426
|
+
outline-color: Highlight;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.ct-aperture-choice:has(input:checked) {
|
|
430
|
+
border: 2px solid Highlight;
|
|
431
|
+
text-decoration: underline;
|
|
432
|
+
text-underline-offset: 0.25em;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.ct-aperture-action:disabled,
|
|
436
|
+
.ct-aperture-action:disabled::before,
|
|
437
|
+
.ct-aperture-action:disabled .ct-aperture-action__icon {
|
|
438
|
+
color: GrayText;
|
|
439
|
+
border-color: GrayText;
|
|
440
|
+
}
|
|
441
|
+
}
|
package/components/card.css
CHANGED
|
@@ -33,6 +33,12 @@
|
|
|
33
33
|
|
|
34
34
|
.ct-card__body {
|
|
35
35
|
display: grid;
|
|
36
|
+
/* Content slots hold whatever the product projects into them. An `auto`
|
|
37
|
+
track grows to the widest item's max-content size, so a table, a long
|
|
38
|
+
identifier or a media element pushes the surface past its own width
|
|
39
|
+
instead of scrolling or wrapping inside it. A clamped track keeps the
|
|
40
|
+
slot at the surface width and leaves overflow to the item. */
|
|
41
|
+
grid-template-columns: minmax(0, 1fr);
|
|
36
42
|
gap: var(--space-4);
|
|
37
43
|
}
|
|
38
44
|
|
package/components/drawer.css
CHANGED
|
@@ -131,6 +131,9 @@
|
|
|
131
131
|
.ct-drawer__body {
|
|
132
132
|
padding: var(--space-6) var(--space-7);
|
|
133
133
|
display: grid;
|
|
134
|
+
/* Clamped content track: projected content wraps or scrolls inside the
|
|
135
|
+
slot instead of widening the surface. See `.ct-card__body`. */
|
|
136
|
+
grid-template-columns: minmax(0, 1fr);
|
|
134
137
|
gap: var(--space-4);
|
|
135
138
|
overflow-y: auto;
|
|
136
139
|
flex: 1;
|
package/components/field.css
CHANGED