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.
- package/.claude/settings.json +3 -1
- package/.claude/skills/components/{data-grid.md → auto-grid.md} +36 -34
- package/.claude/skills/components/display-avatar.md +187 -0
- package/.claude/skills/components/display-chip.md +213 -0
- package/.claude/skills/css-animation-utilities.md +87 -0
- package/.claude/skills/index.md +4 -1
- package/app/assets/styles/setup/06.utility-classes/animations/_animation-scroller-x.css +21 -0
- package/app/assets/styles/setup/06.utility-classes/animations/_auto-rotate.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/_entry-exit-blur.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/_entry-zoom-reveal.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/index.css +5 -4
- package/app/components/01.atoms/display-avatar/DisplayAvatar.vue +130 -0
- package/app/components/{display-avatar → 01.atoms/display-avatar}/stories/DisplayAvatar.stories.ts +12 -0
- package/app/components/01.atoms/display-avatar/tests/DisplayAvatar.spec.ts +208 -0
- package/app/components/01.atoms/display-avatar/tests/__snapshots__/DisplayAvatar.spec.ts.snap +11 -0
- package/app/components/01.atoms/grids/data-grid/AutoGrid.vue +57 -0
- package/app/components/01.atoms/grids/data-grid/stories/{DataGrid.stories.ts → AutoGrid.stories.ts} +95 -39
- package/app/components/01.atoms/grids/data-grid/tests/{DataGrid.spec.ts → AutoGrid.spec.ts} +42 -22
- package/app/components/01.atoms/grids/data-grid/tests/__snapshots__/AutoGrid.spec.ts.snap +11 -0
- package/app/components/01.atoms/grids/data-grid/tests/__snapshots__/DataGrid.spec.ts.snap +3 -3
- package/app/components/02.molecules/display-chip/DisplayChip.vue +189 -0
- package/app/components/{display-chip → 02.molecules/display-chip}/stories/DisplayChip.stories.ts +37 -53
- package/app/components/02.molecules/display-chip/tests/DisplayChip.spec.ts +191 -0
- package/app/components/02.molecules/display-chip/tests/__snapshots__/DisplayChip.spec.ts.snap +12 -0
- package/app/pages/auto-grid.vue +311 -0
- package/app/pages/index.vue +5 -0
- package/app/pages/ui/display-chip.vue +201 -66
- package/package.json +1 -1
- package/app/components/01.atoms/grids/data-grid/DataGrid.vue +0 -39
- package/app/components/display-avatar/DisplayAvatar.vue +0 -148
- package/app/components/display-chip/DisplayChip.vue +0 -187
package/.claude/settings.json
CHANGED
|
@@ -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
|
-
#
|
|
1
|
+
# AutoGrid Component
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
`
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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)
|
|
38
|
-
|
|
39
|
-
| `tag`
|
|
40
|
-
| `:style-class-passthrough` | `string \| string[]`
|
|
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
|
|
49
|
-
|
|
50
|
-
| `--
|
|
51
|
-
| `--
|
|
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
|
-
<
|
|
58
|
+
<AutoGrid style="grid-template-columns: repeat(3, 1fr); --auto-grid-gap: 2.4rem;">
|
|
57
59
|
...
|
|
58
|
-
</
|
|
60
|
+
</AutoGrid>
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
### Narrower minimum item width
|
|
62
64
|
|
|
63
65
|
```vue
|
|
64
|
-
<
|
|
66
|
+
<AutoGrid style="--auto-grid-min-col-size: 180px;">
|
|
65
67
|
...
|
|
66
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
91
|
+
</AutoGrid>
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
### Semantic section with auto aria-labelledby
|
|
93
95
|
|
|
94
96
|
```vue
|
|
95
|
-
<
|
|
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
|
-
</
|
|
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",
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
142
|
+
<AutoGrid :style-class-passthrough="['my-auto-grid']">
|
|
141
143
|
...
|
|
142
|
-
</
|
|
144
|
+
</AutoGrid>
|
|
143
145
|
|
|
144
146
|
<style>
|
|
145
|
-
/* ───
|
|
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
|
-
.
|
|
150
|
-
&.my-
|
|
151
|
-
--
|
|
152
|
-
--
|
|
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
|
-
- `--
|
|
167
|
-
-
|
|
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.
|