srcdev-nuxt-components 9.1.42 → 9.1.44

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.
Files changed (31) hide show
  1. package/.claude/settings.json +3 -1
  2. package/.claude/skills/components/{data-grid.md → auto-grid.md} +36 -34
  3. package/.claude/skills/components/display-avatar.md +187 -0
  4. package/.claude/skills/components/display-chip.md +213 -0
  5. package/.claude/skills/css-animation-utilities.md +87 -0
  6. package/.claude/skills/index.md +4 -1
  7. package/app/assets/styles/setup/06.utility-classes/animations/_animation-scroller-x.css +21 -0
  8. package/app/assets/styles/setup/06.utility-classes/animations/_auto-rotate.css +5 -3
  9. package/app/assets/styles/setup/06.utility-classes/animations/_entry-exit-blur.css +5 -3
  10. package/app/assets/styles/setup/06.utility-classes/animations/_entry-zoom-reveal.css +5 -3
  11. package/app/assets/styles/setup/06.utility-classes/animations/index.css +5 -4
  12. package/app/components/01.atoms/display-avatar/DisplayAvatar.vue +130 -0
  13. package/app/components/{display-avatar → 01.atoms/display-avatar}/stories/DisplayAvatar.stories.ts +12 -0
  14. package/app/components/01.atoms/display-avatar/tests/DisplayAvatar.spec.ts +208 -0
  15. package/app/components/01.atoms/display-avatar/tests/__snapshots__/DisplayAvatar.spec.ts.snap +11 -0
  16. package/app/components/01.atoms/grids/data-grid/AutoGrid.vue +57 -0
  17. package/app/components/01.atoms/grids/data-grid/stories/{DataGrid.stories.ts → AutoGrid.stories.ts} +95 -39
  18. package/app/components/01.atoms/grids/data-grid/tests/{DataGrid.spec.ts → AutoGrid.spec.ts} +42 -22
  19. package/app/components/01.atoms/grids/data-grid/tests/__snapshots__/AutoGrid.spec.ts.snap +11 -0
  20. package/app/components/01.atoms/grids/data-grid/tests/__snapshots__/DataGrid.spec.ts.snap +3 -3
  21. package/app/components/02.molecules/display-chip/DisplayChip.vue +189 -0
  22. package/app/components/{display-chip → 02.molecules/display-chip}/stories/DisplayChip.stories.ts +37 -53
  23. package/app/components/02.molecules/display-chip/tests/DisplayChip.spec.ts +191 -0
  24. package/app/components/02.molecules/display-chip/tests/__snapshots__/DisplayChip.spec.ts.snap +12 -0
  25. package/app/pages/auto-grid.vue +311 -0
  26. package/app/pages/index.vue +5 -0
  27. package/app/pages/ui/display-chip.vue +201 -66
  28. package/package.json +1 -1
  29. package/app/components/01.atoms/grids/data-grid/DataGrid.vue +0 -39
  30. package/app/components/display-avatar/DisplayAvatar.vue +0 -148
  31. package/app/components/display-chip/DisplayChip.vue +0 -187
@@ -26,7 +26,9 @@
26
26
  "Bash(node -e ':*)",
27
27
  "Bash(git ls-tree *)",
28
28
  "Bash(node -p \"require\\('./package.json'\\).version\")",
29
- "Bash(git status *)"
29
+ "Bash(git status *)",
30
+ "Bash(cp /Users/simoncornforth/websites/nuxt-components/.claude/skills/components/data-grid.md /Users/simoncornforth/websites/nuxt-components/.claude/skills/components/auto-grid.md)",
31
+ "Bash(rm /Users/simoncornforth/websites/nuxt-components/.claude/skills/components/data-grid.md)"
30
32
  ],
31
33
  "additionalDirectories": []
32
34
  }
@@ -1,8 +1,8 @@
1
- # DataGrid Component
1
+ # AutoGrid Component
2
2
 
3
3
  ## Overview
4
4
 
5
- `DataGrid` is a responsive auto-fit CSS grid wrapper. It renders whatever named slots the consumer provides, auto-fitting columns to a minimum of `250px` each. Column count and gap are controlled via CSS custom properties, making layout adjustments a single-line style override rather than a prop change.
5
+ `AutoGrid` is a responsive auto-fit CSS grid wrapper. It renders whatever named slots the consumer provides, auto-fitting columns to a minimum of `250px` each. Column count and gap are controlled via CSS custom properties, making layout adjustments a single-line style override rather than a prop change.
6
6
 
7
7
  ---
8
8
 
@@ -11,21 +11,21 @@
11
11
  Pass any number of named slots — the component renders each one in document order inside the grid.
12
12
 
13
13
  ```vue
14
- <DataGrid>
14
+ <AutoGrid>
15
15
  <template #item-1><StatCard label="Revenue" value="£24,500" /></template>
16
16
  <template #item-2><StatCard label="Clients" value="142" /></template>
17
17
  <template #item-3><StatCard label="Bookings" value="38" /></template>
18
- </DataGrid>
18
+ </AutoGrid>
19
19
  ```
20
20
 
21
21
  When filling from a data array, use a dynamic slot name in a `v-for`:
22
22
 
23
23
  ```vue
24
- <DataGrid>
24
+ <AutoGrid>
25
25
  <template v-for="(item, i) in stats" #[`item-${i}`] :key="i">
26
26
  <StatCard :label="item.label" :value="item.value" />
27
27
  </template>
28
- </DataGrid>
28
+ </AutoGrid>
29
29
  ```
30
30
 
31
31
  ---
@@ -34,10 +34,10 @@ When filling from a data array, use a dynamic slot name in a `v-for`:
34
34
 
35
35
  > **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:style-class-passthrough`.
36
36
 
37
- | Prop (template form) | Type | Default | Notes |
38
- |---|---|---|---|
39
- | `tag` | `"div" \| "section" \| "article" \| "main"` | `"div"` | Use a semantic tag for page landmark regions. |
40
- | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes on the root element. |
37
+ | Prop (template form) | Type | Default | Notes |
38
+ | -------------------------- | ------------------------------------------- | ------- | --------------------------------------------- |
39
+ | `tag` | `"div" \| "section" \| "article" \| "main"` | `"div"` | Use a semantic tag for page landmark regions. |
40
+ | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes on the root element. |
41
41
 
42
42
  ---
43
43
 
@@ -45,25 +45,27 @@ When filling from a data array, use a dynamic slot name in a `v-for`:
45
45
 
46
46
  Override these via `style` attribute or a `styleClassPassthrough` class in a consuming `<style>` block.
47
47
 
48
- | Property | Default | Notes |
49
- |---|---|---|
50
- | `--data-grid-columns` | `repeat(auto-fit, minmax(250px, 1fr))` | Full `grid-template-columns` value. Override to fix column count or change min width. |
51
- | `--data-grid-gap` | `1rem` | Grid gap between items. |
48
+ | Property | Default | Notes |
49
+ | ------------------------- | -------- | ---------------------------------------------------------------------------------------- |
50
+ | `--auto-grid-min-col-size` | `250px` | Minimum column width; browser auto-fits as many columns as will fit. |
51
+ | `--auto-grid-gap` | `1rem` | Grid gap between items. |
52
52
 
53
53
  ### Fixed column count
54
54
 
55
+ Override `grid-template-columns` directly — there is no single token for this:
56
+
55
57
  ```vue
56
- <DataGrid style="--data-grid-columns: repeat(3, 1fr); --data-grid-gap: 2.4rem;">
58
+ <AutoGrid style="grid-template-columns: repeat(3, 1fr); --auto-grid-gap: 2.4rem;">
57
59
  ...
58
- </DataGrid>
60
+ </AutoGrid>
59
61
  ```
60
62
 
61
63
  ### Narrower minimum item width
62
64
 
63
65
  ```vue
64
- <DataGrid style="--data-grid-columns: repeat(auto-fit, minmax(180px, 1fr));">
66
+ <AutoGrid style="--auto-grid-min-col-size: 180px;">
65
67
  ...
66
- </DataGrid>
68
+ </AutoGrid>
67
69
  ```
68
70
 
69
71
  ---
@@ -73,7 +75,7 @@ Override these via `style` attribute or a `styleClassPassthrough` class in a con
73
75
  ### Stat cards (default auto-fit)
74
76
 
75
77
  ```vue
76
- <DataGrid>
78
+ <AutoGrid>
77
79
  <template #revenue>
78
80
  <div class="stat-card">
79
81
  <span class="stat-card-label">Revenue</span>
@@ -86,17 +88,17 @@ Override these via `style` attribute or a `styleClassPassthrough` class in a con
86
88
  <span class="stat-card-value">142</span>
87
89
  </div>
88
90
  </template>
89
- </DataGrid>
91
+ </AutoGrid>
90
92
  ```
91
93
 
92
94
  ### Semantic section with auto aria-labelledby
93
95
 
94
96
  ```vue
95
- <DataGrid tag="section">
97
+ <AutoGrid tag="section">
96
98
  <!-- aria-labelledby is wired automatically via useAriaLabelledById -->
97
99
  <template #item-1><div>Item 1</div></template>
98
100
  <template #item-2><div>Item 2</div></template>
99
- </DataGrid>
101
+ </AutoGrid>
100
102
  ```
101
103
 
102
104
  ### Data-driven grid
@@ -105,20 +107,20 @@ Override these via `style` attribute or a `styleClassPassthrough` class in a con
105
107
  <script setup lang="ts">
106
108
  const stats = [
107
109
  { id: "revenue", label: "Revenue", value: "£24,500" },
108
- { id: "clients", label: "Clients", value: "142" },
110
+ { id: "clients", label: "Clients", value: "142" },
109
111
  { id: "bookings", label: "Bookings", value: "38" },
110
112
  ];
111
113
  </script>
112
114
 
113
115
  <template>
114
- <DataGrid>
116
+ <AutoGrid>
115
117
  <template v-for="stat in stats" #[stat.id] :key="stat.id">
116
118
  <div class="stat-card">
117
119
  <span class="stat-card-label">{{ stat.label }}</span>
118
120
  <span class="stat-card-value">{{ stat.value }}</span>
119
121
  </div>
120
122
  </template>
121
- </DataGrid>
123
+ </AutoGrid>
122
124
  </template>
123
125
  ```
124
126
 
@@ -137,19 +139,19 @@ See [component-aria-landmark.md](../component-aria-landmark.md) for the full lan
137
139
  ## Local style override scaffold
138
140
 
139
141
  ```vue
140
- <DataGrid :style-class-passthrough="['my-data-grid']">
142
+ <AutoGrid :style-class-passthrough="['my-auto-grid']">
141
143
  ...
142
- </DataGrid>
144
+ </AutoGrid>
143
145
 
144
146
  <style>
145
- /* ─── DataGrid local overrides ──────────────────────────────────────
147
+ /* ─── AutoGrid local overrides ──────────────────────────────────────
146
148
  Use CSS custom properties for layout, not utility classes.
147
149
  Delete this block if no overrides are needed.
148
150
  ─────────────────────────────────────────────────────────────────── */
149
- .data-grid {
150
- &.my-data-grid {
151
- --data-grid-columns: repeat(auto-fit, minmax(200px, 1fr));
152
- --data-grid-gap: 2rem;
151
+ .auto-grid {
152
+ &.my-auto-grid {
153
+ --auto-grid-min-col-size: 200px;
154
+ --auto-grid-gap: 2rem;
153
155
  }
154
156
  }
155
157
  </style>
@@ -163,5 +165,5 @@ See [component-local-style-override.md](../component-local-style-override.md) fo
163
165
 
164
166
  - Auto-imported in Nuxt — no manual import needed.
165
167
  - Slot names can be anything — semantic (`#revenue`) or indexed (`#item-0`). Document order determines render order.
166
- - `--data-grid-columns` accepts any valid `grid-template-columns` value, including named tracks and `subgrid`.
167
- - The component does not impose a column count `auto-fit` with `minmax` means the browser decides. Use `repeat(N, 1fr)` in `--data-grid-columns` to fix the count.
168
+ - `--auto-grid-min-col-size` controls the minimum column width; `auto-fit` fills as many columns as will fit.
169
+ - To fix the column count, override `grid-template-columns` directly (e.g. `style="grid-template-columns: repeat(3, 1fr)"`) there is no single token for this.
@@ -0,0 +1,187 @@
1
+ # DisplayAvatar Component
2
+
3
+ ## Overview
4
+
5
+ `DisplayAvatar` renders a circular avatar — either an image (via `NuxtImg`) or a text fallback showing initials derived from the `alt` prop. Optionally wraps in a `DisplayChip` to show a status indicator badge.
6
+
7
+ ---
8
+
9
+ ## Props reference
10
+
11
+ > **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:style-class-passthrough`.
12
+
13
+ | Prop (template form) | Type | Default | Notes |
14
+ | -------------------------- | ----------------------------------------- | -------- | ------------------------------------------------------------------ |
15
+ | `as` | `string \| object` | `"span"` | Root element tag. Ignored when `chip` is set. |
16
+ | `src` | `string` | — | Image URL. Renders `NuxtImg` when set; fallback text otherwise. |
17
+ | `alt` | `string` | — | Alt text for the image; also used to derive initials. |
18
+ | `text` | `string` | — | Override the auto-derived initials with an explicit string. |
19
+ | `size` | `"xs" \| "s" \| "md" \| "lg" \| "xl"` | `"md"` | Controls width, height, and font-size. |
20
+ | `chip` | `boolean \| DisplayChipConfig` | — | Add a status chip. `true` uses defaults; pass a config object to customise. |
21
+ | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes on the root element. |
22
+
23
+ ### Size dimensions
24
+
25
+ | Size | Diameter | Font size |
26
+ | ---- | -------- | --------- |
27
+ | `xs` | 24px | 0.75rem |
28
+ | `s` | 32px | 0.875rem |
29
+ | `md` | 40px | 1rem |
30
+ | `lg` | 48px | 1.125rem |
31
+ | `xl` | 56px | 1.25rem |
32
+
33
+ ---
34
+
35
+ ## Slots
36
+
37
+ | Slot | Purpose |
38
+ | --------- | -------------------------------------------------------------------- |
39
+ | `default` | Replaces the auto image/fallback content entirely. |
40
+ | `icon` | Appended inside the avatar (e.g. an icon overlay over the image). |
41
+
42
+ ---
43
+
44
+ ## Fallback text logic
45
+
46
+ When `src` is not set, a `<span>` renders the fallback value:
47
+
48
+ 1. `text` prop — used as-is if provided.
49
+ 2. `alt` initials — first character of each word, capped at two characters.
50
+ 3. Empty string — if neither is set.
51
+
52
+ ```
53
+ alt="John Doe" → "JD"
54
+ alt="Alice" → "A"
55
+ alt="Alice Bob C" → "AB"
56
+ text="?" → "?"
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Usage examples
62
+
63
+ ### Image avatar
64
+
65
+ ```vue
66
+ <DisplayAvatar
67
+ src="/images/profile.jpg"
68
+ alt="Jane Smith"
69
+ size="lg"
70
+ />
71
+ ```
72
+
73
+ ### Initials fallback
74
+
75
+ ```vue
76
+ <DisplayAvatar alt="Jane Smith" size="md" />
77
+ <!-- renders: "JS" -->
78
+ ```
79
+
80
+ ### Custom text fallback
81
+
82
+ ```vue
83
+ <DisplayAvatar text="?" size="xs" />
84
+ ```
85
+
86
+ ### Custom root element
87
+
88
+ ```vue
89
+ <DisplayAvatar as="div" alt="Jane Smith" />
90
+ ```
91
+
92
+ ### With a status chip (default config)
93
+
94
+ ```vue
95
+ <DisplayAvatar
96
+ src="/images/profile.jpg"
97
+ alt="Jane Smith"
98
+ :chip="true"
99
+ />
100
+ ```
101
+
102
+ Default chip config: `{ size: "12px", maskWidth: "4px", offset: "0px", angle: "90deg" }`.
103
+
104
+ ### With a custom chip
105
+
106
+ ```vue
107
+ <DisplayAvatar
108
+ src="/images/profile.jpg"
109
+ alt="Jane Smith"
110
+ :chip="{
111
+ size: '16px',
112
+ maskWidth: '2px',
113
+ offset: '4px',
114
+ angle: '45deg'
115
+ }"
116
+ />
117
+ ```
118
+
119
+ Full `DisplayChipConfig` shape (pass directly as the `chip` value):
120
+
121
+ ```ts
122
+ interface DisplayChipConfig {
123
+ size: string // chip diameter, e.g. "12px"
124
+ maskWidth: string // cutout ring width, e.g. "4px"
125
+ offset: string // distance from avatar edge, e.g. "0px"
126
+ angle: string // position around avatar (0–360deg), e.g. "45deg"
127
+ icon?: string // Iconify icon name
128
+ label?: string // short text (max 3 characters)
129
+ }
130
+ ```
131
+
132
+ ### Default slot override
133
+
134
+ ```vue
135
+ <DisplayAvatar size="xl">
136
+ <template #default>
137
+ <img src="/images/profile.jpg" alt="Jane Smith" class="avatar-image" />
138
+ </template>
139
+ </DisplayAvatar>
140
+ ```
141
+
142
+ ### Icon slot
143
+
144
+ ```vue
145
+ <DisplayAvatar alt="Jane Smith">
146
+ <template #icon>
147
+ <Icon name="bi:check-circle-fill" class="avatar-icon" />
148
+ </template>
149
+ </DisplayAvatar>
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Local style override scaffold
155
+
156
+ ```vue
157
+ <DisplayAvatar
158
+ alt="Jane Smith"
159
+ :style-class-passthrough="['profile-avatar']"
160
+ />
161
+
162
+ <style>
163
+ /* ─── DisplayAvatar local overrides ────────────────────────────────
164
+ Scope by your wrapper class, then nest .display-avatar directly.
165
+ No :deep() needed (component styles are unscoped).
166
+ Delete this block if no overrides are needed.
167
+ ─────────────────────────────────────────────────────────────────── */
168
+ .my-page-section {
169
+ .display-avatar {
170
+ &.profile-avatar {
171
+ /* custom overrides */
172
+ }
173
+ }
174
+ }
175
+ </style>
176
+ ```
177
+
178
+ See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
179
+
180
+ ---
181
+
182
+ ## Notes
183
+
184
+ - Auto-imported in Nuxt — no manual import needed.
185
+ - When `chip` is set, the root element becomes `DisplayChip` and the `as` prop is ignored.
186
+ - `class` and `style` are **not** declared as explicit props — they fall through to the root element automatically via Vue's attribute inheritance (`inheritAttrs: true`). Do not re-add them as props; doing so pulls them out of `$attrs` and breaks automatic inheritance.
187
+ - `NuxtImg` is used for the image, so `@nuxt/image` must be installed in the consuming app.
@@ -0,0 +1,213 @@
1
+ # DisplayChip Component
2
+
3
+ ## Overview
4
+
5
+ `DisplayChip` renders a small status indicator dot (or icon/label badge) that is absolutely positioned on a parent element using CSS trigonometric functions. It works by applying a radial-gradient mask to the parent's content, creating a clean cutout behind the chip. Supports circle and square parent shapes.
6
+
7
+ Used directly for standalone chip overlays, and internally by `DisplayAvatar` when its `chip` prop is set.
8
+
9
+ ---
10
+
11
+ ## Props reference
12
+
13
+ > **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:style-class-passthrough`.
14
+
15
+ | Prop (template form) | Type | Default | Notes |
16
+ | -------------------------- | -------------------------- | ---------- | -------------------------------------------------- |
17
+ | `tag` | `"div" \| "span"` | `"span"` | Root element tag. |
18
+ | `shape` | `"circle" \| "square"` | `"circle"` | Affects position maths — must match the parent shape. |
19
+ | `:config` | `DisplayChipConfig` | see below | Controls chip geometry and optional content. |
20
+ | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes — use for status colour variants. |
21
+
22
+ ### DisplayChipConfig
23
+
24
+ ```ts
25
+ interface DisplayChipConfig {
26
+ size: string // chip dot diameter, e.g. "12px"
27
+ maskWidth: string // cutout ring width around the chip, e.g. "4px"
28
+ offset: string // extra distance from the parent edge, e.g. "0px"
29
+ angle: string // position around the parent (0–360deg), e.g. "45deg"
30
+ icon?: string // Iconify icon name rendered inside the chip
31
+ label?: string // short text rendered inside the chip (max 3 characters)
32
+ }
33
+ ```
34
+
35
+ Default config: `{ size: "12px", maskWidth: "4px", offset: "0px", angle: "90deg" }`.
36
+
37
+ ### Angle reference
38
+
39
+ | Angle | Position |
40
+ | -------- | ------------ |
41
+ | `0deg` | Top |
42
+ | `45deg` | Top-right |
43
+ | `90deg` | Right |
44
+ | `135deg` | Bottom-right |
45
+ | `180deg` | Bottom |
46
+ | `225deg` | Bottom-left |
47
+ | `270deg` | Left |
48
+ | `315deg` | Top-left |
49
+
50
+ ---
51
+
52
+ ## Status colours
53
+
54
+ Apply status via `styleClassPassthrough` — the component has built-in colour variants:
55
+
56
+ | Class | Colour |
57
+ | ---------- | ----------------------- |
58
+ | (none) | `slategrey` (offline) |
59
+ | `online` | `rgb(0, 255, 135)` |
60
+ | `idle` | `rgb(255, 185, 51)` |
61
+ | `dnd` | `rgb(255, 40, 80)` |
62
+
63
+ ```vue
64
+ <DisplayChip :style-class-passthrough="['online']">...</DisplayChip>
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Label constraints
70
+
71
+ - Max 3 characters. Longer values are silently truncated with a `console.warn`.
72
+ - Font-size scales automatically with chip size via `--_font-size-adjust`:
73
+ - 1 char → `0.7 × size`
74
+ - 2 chars → `0.6 × size`
75
+ - 3 chars → `0.5 × size`
76
+
77
+ ---
78
+
79
+ ## Slots
80
+
81
+ | Slot | Purpose |
82
+ | --------- | ------------------------------------------ |
83
+ | `default` | The host element the chip is positioned on. Must be a single block element. |
84
+
85
+ ---
86
+
87
+ ## Usage examples
88
+
89
+ ### Simple status dot on a circular avatar
90
+
91
+ ```vue
92
+ <DisplayChip
93
+ shape="circle"
94
+ :config="{ size: '12px', maskWidth: '4px', offset: '0px', angle: '45deg' }"
95
+ :style-class-passthrough="['online']"
96
+ >
97
+ <div class="avatar">SRC</div>
98
+ </DisplayChip>
99
+ ```
100
+
101
+ ### Status dot on a square card thumbnail
102
+
103
+ ```vue
104
+ <DisplayChip
105
+ shape="square"
106
+ :config="{ size: '10px', maskWidth: '3px', offset: '2px', angle: '135deg' }"
107
+ :style-class-passthrough="['idle']"
108
+ >
109
+ <img src="/thumbnail.jpg" alt="Card thumbnail" />
110
+ </DisplayChip>
111
+ ```
112
+
113
+ ### With an icon inside the chip
114
+
115
+ ```vue
116
+ <DisplayChip
117
+ :config="{ size: '16px', maskWidth: '4px', offset: '0px', angle: '45deg', icon: 'bi:check-circle-fill' }"
118
+ :style-class-passthrough="['online']"
119
+ >
120
+ <div class="avatar">SRC</div>
121
+ </DisplayChip>
122
+ ```
123
+
124
+ ### With a label inside the chip
125
+
126
+ ```vue
127
+ <!-- 1–3 characters only; longer values are truncated with a warning -->
128
+ <DisplayChip
129
+ :config="{ size: '16px', maskWidth: '4px', offset: '0px', angle: '45deg', label: '+2' }"
130
+ :style-class-passthrough="['dnd']"
131
+ >
132
+ <div class="avatar">SRC</div>
133
+ </DisplayChip>
134
+ ```
135
+
136
+ ### Reactive config (QA panel / form pattern)
137
+
138
+ ```vue
139
+ <script setup lang="ts">
140
+ import type { DisplayChipConfig } from 'srcdev-nuxt-components/types/components'
141
+
142
+ const size = ref(12)
143
+ const angle = ref(45)
144
+
145
+ const chipConfig = computed((): DisplayChipConfig => ({
146
+ size: `${size.value}px`,
147
+ maskWidth: '4px',
148
+ offset: '0px',
149
+ angle: `${angle.value}deg`,
150
+ }))
151
+ </script>
152
+
153
+ <template>
154
+ <DisplayChip shape="circle" :config="chipConfig" :style-class-passthrough="['online']">
155
+ <div class="avatar">SRC</div>
156
+ </DisplayChip>
157
+ </template>
158
+ ```
159
+
160
+ ### Via DisplayAvatar (recommended for avatar use cases)
161
+
162
+ Prefer `DisplayAvatar` with its `chip` prop over wiring `DisplayChip` directly:
163
+
164
+ ```vue
165
+ <DisplayAvatar
166
+ src="/images/profile.jpg"
167
+ alt="Jane Smith"
168
+ :chip="{ size: '12px', maskWidth: '4px', offset: '0px', angle: '45deg' }"
169
+ :style-class-passthrough="['online']"
170
+ />
171
+ ```
172
+
173
+ See [display-avatar.md](./display-avatar.md) for the full API.
174
+
175
+ ---
176
+
177
+ ## Local style override scaffold
178
+
179
+ ```vue
180
+ <DisplayChip
181
+ :config="chipConfig"
182
+ :style-class-passthrough="['my-chip']"
183
+ >
184
+ <div class="avatar">SRC</div>
185
+ </DisplayChip>
186
+
187
+ <style>
188
+ /* ─── DisplayChip local overrides ──────────────────────────────────
189
+ Scope by your wrapper class, then nest .display-chip-core directly.
190
+ No :deep() needed (component styles are unscoped).
191
+ Delete this block if no overrides are needed.
192
+ ─────────────────────────────────────────────────────────────────── */
193
+ .my-page-section {
194
+ .display-chip-core {
195
+ &.my-chip {
196
+ /* override colour vars, e.g. */
197
+ --color-online: hotpink;
198
+ }
199
+ }
200
+ }
201
+ </style>
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Notes
207
+
208
+ - Auto-imported in Nuxt — no manual import needed.
209
+ - `shape` must match the actual shape of the slot content — the position maths differs between `circle` (radius-based) and `square` (clamped corner-aware).
210
+ - `config` values are geometric inputs to CSS `calc(cos())` / `calc(sin())` expressions. Pass them as strings with units (`"12px"`, `"45deg"`), not plain numbers.
211
+ - The chip dot is rendered via `::after` pseudo-element; icon and label sit above it at `z-index: 2`.
212
+ - The mask cutout is applied to all direct children of `.display-chip-core` except `.chip-icon` and `.chip-label` — ensure the host element is a direct child.
213
+ - `DisplayChipConfig` and `DisplayChipProps` are both exported from the layer types. Use `DisplayChipConfig` when passing geometry values (the `config` prop). Use `DisplayChipProps` only if you need to pass the full component prop set (e.g. when building a wrapper component).
@@ -0,0 +1,87 @@
1
+ # CSS Animation Utilities
2
+
3
+ ## Overview
4
+
5
+ Scroll-driven animation utility classes bundled with the layer. Apply a class to any element to get a CSS-only, scroll-linked animation — no JavaScript required. All utilities use the [CSS Scroll-Driven Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline) spec (`animation-timeline: view()`).
6
+
7
+ **Browser support**: Chrome 115+, Firefox 110+, Safari 18+. No polyfill exists — use `@supports` for graceful degradation where needed.
8
+
9
+ All utilities are wrapped in `@media (prefers-reduced-motion: no-preference)` — animations are automatically disabled for users who prefer reduced motion.
10
+
11
+ ## Available classes
12
+
13
+ ### `.animation-scroller-x`
14
+
15
+ Scales and fades items relative to their position in a **horizontal scroll container** (carousel, horizontal list). Items at the edges are small and faint; items at the centre are full size and opaque.
16
+
17
+ ```css
18
+ animation-timeline: view(x);
19
+ ```
20
+
21
+ | Position | `opacity` | `scale` |
22
+ |---|---|---|
23
+ | Edges (0 %, 100 %) | 0.25 | 0.5 |
24
+ | Centre (35 %–65 %) | 1 | 1 |
25
+
26
+ #### Usage
27
+
28
+ ```html
29
+ <!-- Scrollable container — overflow-x must be auto or scroll -->
30
+ <div class="carousel-track" style="overflow-x: auto; display: flex;">
31
+ <div class="animation-scroller-x">Item 1</div>
32
+ <div class="animation-scroller-x">Item 2</div>
33
+ <div class="animation-scroller-x">Item 3</div>
34
+ </div>
35
+ ```
36
+
37
+ The animation is driven by the element's position inside its nearest scrollport on the x-axis. Add `.animation-scroller-x` to each **child** — not the container.
38
+
39
+ ---
40
+
41
+ ### `.entry-zoom-reveal`
42
+
43
+ Fades and zooms an element in as it scrolls into the vertical viewport (bottom 30 % inset to top 5 %).
44
+
45
+ ```html
46
+ <div class="entry-zoom-reveal">Content revealed on scroll</div>
47
+ ```
48
+
49
+ - `fill: both` — element stays hidden before entry, visible after exit
50
+
51
+ ---
52
+
53
+ ### `.entry-slide-in`
54
+
55
+ Slides an element up from 200 px below as it scrolls into view.
56
+
57
+ ```html
58
+ <div class="entry-slide-in">Slides up on scroll</div>
59
+ ```
60
+
61
+ ---
62
+
63
+ ### `.entry-exit-blur`
64
+
65
+ Blurs an element as it enters and exits the vertical viewport; sharp in the centre (45 %–55 %).
66
+
67
+ ```html
68
+ <div class="entry-exit-blur">Sharp in view, blurred at edges</div>
69
+ ```
70
+
71
+ ---
72
+
73
+ ### `.auto-rotate`
74
+
75
+ Rotates an element 0 → 360 ° as it scrolls through the vertical viewport.
76
+
77
+ ```html
78
+ <div class="auto-rotate">
79
+ <img src="/logo.svg" alt="" />
80
+ </div>
81
+ ```
82
+
83
+ ## Notes
84
+
85
+ - All classes use `animation-timeline: view()` (vertical) **except** `.animation-scroller-x` which uses `view(x)`.
86
+ - The `scroller` keyframe name used by `.animation-scroller-x` is global — avoid re-declaring `@keyframes scroller` in your own CSS.
87
+ - Utility classes are included automatically when you extend the layer — no explicit import needed in your app.