@spaethtech/svelte-ui 0.3.1-dev.6.5d93a69 → 0.3.1-dev.7.3dc4fbb
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/skills/svelte-ui/SKILL.md +116 -0
- package/.claude/skills/themes/SKILL.md +303 -0
- package/CLAUDE.md +248 -0
- package/docs/components.md +240 -0
- package/docs/examples.md +323 -0
- package/docs/paradigm.md +240 -0
- package/docs/themes.md +170 -0
- package/docs/usage.md +427 -0
- package/package.json +6 -2
package/docs/paradigm.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Development Paradigm
|
|
2
|
+
|
|
3
|
+
This project uses **specification-driven development** (a form of behavior-driven development) where specifications are written before implementation. This paradigm ensures clear requirements, consistent implementation, and better testing.
|
|
4
|
+
|
|
5
|
+
## Workflow Overview
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph LR
|
|
9
|
+
A[Write Spec] --> B[Review & Approve]
|
|
10
|
+
B --> C[Implement Component]
|
|
11
|
+
C --> D[Validate Against Spec]
|
|
12
|
+
D --> E[Write Tests]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Interactive Commands
|
|
16
|
+
|
|
17
|
+
### `/specify <component-name>`
|
|
18
|
+
|
|
19
|
+
Creates a new component specification interactively.
|
|
20
|
+
|
|
21
|
+
- Prompts for purpose, API, behavior, etc.
|
|
22
|
+
- Generates `.spec.md` file
|
|
23
|
+
- Sets up component folder structure
|
|
24
|
+
|
|
25
|
+
### `/implement <component-name>`
|
|
26
|
+
|
|
27
|
+
Implements a component from its specification.
|
|
28
|
+
|
|
29
|
+
- Reads existing `.spec.md` file
|
|
30
|
+
- Creates Svelte component following spec
|
|
31
|
+
- Optionally creates tests and demo page
|
|
32
|
+
|
|
33
|
+
### `/validate <component-name>`
|
|
34
|
+
|
|
35
|
+
Validates implementation against specification.
|
|
36
|
+
|
|
37
|
+
- Compares implementation with spec
|
|
38
|
+
- Checks acceptance criteria
|
|
39
|
+
- Reports discrepancies
|
|
40
|
+
- Can auto-fix issues
|
|
41
|
+
|
|
42
|
+
## Manual Workflow
|
|
43
|
+
|
|
44
|
+
### 1. Write Specification
|
|
45
|
+
|
|
46
|
+
Create `src/lib/components/ComponentName/ComponentName.spec.md`:
|
|
47
|
+
|
|
48
|
+
```markdown
|
|
49
|
+
# Component Specification: ComponentName
|
|
50
|
+
|
|
51
|
+
## Overview
|
|
52
|
+
|
|
53
|
+
- **Purpose**: What the component does
|
|
54
|
+
- **User Story**: As a [user], I want to [action] so that [benefit]
|
|
55
|
+
|
|
56
|
+
## API Contract
|
|
57
|
+
|
|
58
|
+
### Props
|
|
59
|
+
|
|
60
|
+
- `propName` (type, required/optional): Description
|
|
61
|
+
- Default values and constraints
|
|
62
|
+
|
|
63
|
+
### Events
|
|
64
|
+
|
|
65
|
+
- `eventName`: When triggered, payload structure
|
|
66
|
+
|
|
67
|
+
### Slots
|
|
68
|
+
|
|
69
|
+
- Slot name and purpose
|
|
70
|
+
|
|
71
|
+
## Behavior Specifications
|
|
72
|
+
|
|
73
|
+
### Visual States
|
|
74
|
+
|
|
75
|
+
- Default appearance
|
|
76
|
+
- Interactive states (hover, focus, active)
|
|
77
|
+
- Variant appearances
|
|
78
|
+
|
|
79
|
+
### User Interactions
|
|
80
|
+
|
|
81
|
+
- Click behavior
|
|
82
|
+
- Keyboard navigation
|
|
83
|
+
- Touch interactions
|
|
84
|
+
|
|
85
|
+
### Accessibility Requirements
|
|
86
|
+
|
|
87
|
+
- ARIA attributes
|
|
88
|
+
- Keyboard support
|
|
89
|
+
- Screen reader behavior
|
|
90
|
+
|
|
91
|
+
## Performance Constraints
|
|
92
|
+
|
|
93
|
+
- Bundle size limits
|
|
94
|
+
- Rendering performance
|
|
95
|
+
- Memory usage
|
|
96
|
+
|
|
97
|
+
## Edge Cases
|
|
98
|
+
|
|
99
|
+
- Empty states
|
|
100
|
+
- Error states
|
|
101
|
+
- Boundary conditions
|
|
102
|
+
|
|
103
|
+
## Acceptance Criteria
|
|
104
|
+
|
|
105
|
+
- [ ] Specific, testable requirements
|
|
106
|
+
- [ ] Each item should be verifiable
|
|
107
|
+
- [ ] Clear pass/fail conditions
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 2. Implement Component
|
|
111
|
+
|
|
112
|
+
Create `src/lib/components/ComponentName/ComponentName.svelte`:
|
|
113
|
+
|
|
114
|
+
```svelte
|
|
115
|
+
<script lang="ts">
|
|
116
|
+
// Follow the specification exactly
|
|
117
|
+
interface Props {
|
|
118
|
+
// Props as defined in spec
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let {} /* props */ : Props = $props();
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<!-- Implementation matching spec -->
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Create Tests
|
|
128
|
+
|
|
129
|
+
Tests use **Playwright** (the project's test runner — `npm run test`). Create
|
|
130
|
+
`src/lib/components/ComponentName/ComponentName.test.ts`:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { test, expect } from "@playwright/test";
|
|
134
|
+
|
|
135
|
+
test.describe("ComponentName - Spec Validation", () => {
|
|
136
|
+
// Test each acceptance criterion against the rendered demo page.
|
|
137
|
+
test("meets acceptance criterion 1", async ({ page }) => {
|
|
138
|
+
await page.goto("/component-name");
|
|
139
|
+
// assertions…
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 4. Export Component
|
|
145
|
+
|
|
146
|
+
Add to `src/lib/index.ts`:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
export { default as ComponentName } from "./components/ComponentName/ComponentName.svelte";
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Component Structure
|
|
153
|
+
|
|
154
|
+
### Colocated Structure (Recommended)
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
src/lib/components/
|
|
158
|
+
└── ComponentName/
|
|
159
|
+
├── ComponentName.spec.md # Write first
|
|
160
|
+
├── ComponentName.svelte # Implement second
|
|
161
|
+
├── ComponentName.test.ts # Test third
|
|
162
|
+
└── index.ts # Export
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Benefits
|
|
166
|
+
|
|
167
|
+
1. **Clear Requirements**: Specs define exactly what to build
|
|
168
|
+
2. **Consistent Implementation**: All components follow same pattern
|
|
169
|
+
3. **Better Testing**: Tests validate against documented requirements
|
|
170
|
+
4. **Living Documentation**: Specs serve as component documentation
|
|
171
|
+
5. **Reduced Iterations**: Clear specs mean fewer revisions
|
|
172
|
+
|
|
173
|
+
## Example: Badge Component
|
|
174
|
+
|
|
175
|
+
### 1. Specification (Badge.spec.md)
|
|
176
|
+
|
|
177
|
+
```markdown
|
|
178
|
+
# Component Specification: Badge
|
|
179
|
+
|
|
180
|
+
## Overview
|
|
181
|
+
|
|
182
|
+
- **Purpose**: Display status indicators or labels
|
|
183
|
+
- **User Story**: As a developer, I want to display badges with consistent styling
|
|
184
|
+
|
|
185
|
+
## API Contract
|
|
186
|
+
|
|
187
|
+
### Props
|
|
188
|
+
|
|
189
|
+
- `text` (string, required): Display text
|
|
190
|
+
- `variant` (string, optional): Visual style
|
|
191
|
+
- `size` (string, optional): Size variant
|
|
192
|
+
- `dismissible` (boolean, optional): Show dismiss button
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 2. Implementation (Badge.svelte)
|
|
196
|
+
|
|
197
|
+
```svelte
|
|
198
|
+
<script lang="ts">
|
|
199
|
+
interface Props {
|
|
200
|
+
text: string;
|
|
201
|
+
variant?: "default" | "primary" | "success";
|
|
202
|
+
size?: "sm" | "md" | "lg";
|
|
203
|
+
dismissible?: boolean;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let { text, variant = "default", size = "md", dismissible = false }: Props = $props();
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<span class="badge badge-{variant} badge-{size}">
|
|
210
|
+
{text}
|
|
211
|
+
{#if dismissible}
|
|
212
|
+
<button onclick={() => dispatch("dismiss")}>×</button>
|
|
213
|
+
{/if}
|
|
214
|
+
</span>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Best Practices
|
|
218
|
+
|
|
219
|
+
1. **Write specs before code** - Never implement without a spec
|
|
220
|
+
2. **Include acceptance criteria** - Make requirements testable
|
|
221
|
+
3. **Update specs when requirements change** - Keep documentation current
|
|
222
|
+
4. **Use `/validate` after changes** - Ensure compliance
|
|
223
|
+
5. **Reference specs in PRs** - Link implementation to requirements
|
|
224
|
+
|
|
225
|
+
## Tips for Writing Good Specs
|
|
226
|
+
|
|
227
|
+
- Be specific about behavior, not implementation
|
|
228
|
+
- Include all edge cases
|
|
229
|
+
- Define clear acceptance criteria
|
|
230
|
+
- Consider accessibility from the start
|
|
231
|
+
- Think about theme compatibility
|
|
232
|
+
- Document performance requirements
|
|
233
|
+
|
|
234
|
+
## Common Pitfalls to Avoid
|
|
235
|
+
|
|
236
|
+
- Don't skip the spec phase
|
|
237
|
+
- Don't implement features not in the spec
|
|
238
|
+
- Don't modify specs after implementation (unless updating both)
|
|
239
|
+
- Don't write vague acceptance criteria
|
|
240
|
+
- Don't forget accessibility requirements
|
package/docs/themes.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# CSS Variables Reference
|
|
2
|
+
|
|
3
|
+
This document describes the CSS variables in the svelte-ui theme. The source of truth is
|
|
4
|
+
`src/lib/theme.css` (shipped as `@spaethtech/svelte-ui/theme.css`); this doc mirrors it. Every token is
|
|
5
|
+
**`--ui-*`-prefixed** — there are no unprefixed tokens and no per-component tokens.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The library themes via CSS custom properties. There are **two themes only — light (default) and
|
|
10
|
+
dark**. Dark follows the system preference automatically (`@media (prefers-color-scheme: dark)`);
|
|
11
|
+
force either with the `.theme-light` / `.theme-dark` classes (see `ThemeSelector`).
|
|
12
|
+
|
|
13
|
+
Consumers import the theme right after Tailwind:
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
@import "tailwindcss";
|
|
17
|
+
@import "@spaethtech/svelte-ui/theme.css";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Palette — `--ui-color-*`
|
|
21
|
+
|
|
22
|
+
### Core
|
|
23
|
+
|
|
24
|
+
- `--ui-color-background` — main background for pages and containers
|
|
25
|
+
- `--ui-color-text` — primary text color
|
|
26
|
+
|
|
27
|
+
### Semantic
|
|
28
|
+
|
|
29
|
+
- `--ui-color-primary` — primary brand color (blue)
|
|
30
|
+
- `--ui-color-secondary` — secondary brand color (cyan)
|
|
31
|
+
- `--ui-color-success` — success state (green)
|
|
32
|
+
- `--ui-color-warning` — warning state (amber)
|
|
33
|
+
- `--ui-color-error` — error/danger state (red) — the `danger` variant maps here
|
|
34
|
+
- `--ui-color-info` — informational state (sky blue)
|
|
35
|
+
|
|
36
|
+
## Interactive-surface tints — `--ui-accent` + hover / surface / active
|
|
37
|
+
|
|
38
|
+
One shared tint language for every hover, section band, and pressed surface. Three steps of the
|
|
39
|
+
**same** mix, from lightest to strongest:
|
|
40
|
+
|
|
41
|
+
- `--ui-color-hover` — **4%** — row / option / ghost-button hover
|
|
42
|
+
- `--ui-color-surface` — **8%** — header / footer / section bands (a notch darker than hover)
|
|
43
|
+
- `--ui-color-active` — **12%** — pressed / held
|
|
44
|
+
|
|
45
|
+
Each is `color-mix(in srgb, var(--ui-accent) N%, transparent)`. The **percentages are
|
|
46
|
+
theme-independent** (identical in light and dark) — only the accent color flips.
|
|
47
|
+
|
|
48
|
+
- `--ui-accent` — the color the tints mix from. **Defaults to `var(--ui-color-text)`**, so out of
|
|
49
|
+
the box these are neutral grey tints. Point it at a semantic color to tint a surface (this is how
|
|
50
|
+
a component `variant` colors its whole surface). Overriding `--ui-accent` requires the `.ui-accent`
|
|
51
|
+
re-mix class — see [Applying a theme](#theme-application) and the themes skill.
|
|
52
|
+
|
|
53
|
+
## Border & radius — `--ui-border-*`
|
|
54
|
+
|
|
55
|
+
- `--ui-border-radius` — `0.375rem` — corner radius for every surface (Card, Input, DataTable…)
|
|
56
|
+
- `--ui-border-color` — `color-mix(in srgb, var(--ui-color-secondary) 30%, transparent)` — soft
|
|
57
|
+
internal divider (row separators, section splits)
|
|
58
|
+
- `--ui-border-color-strong` — `var(--ui-color-secondary)` — prominent outer edge (Card frame,
|
|
59
|
+
Input, DataTable outer)
|
|
60
|
+
- `--ui-border-color-strong-hover` — `color-mix(in srgb, var(--ui-color-secondary) 80%, black)` —
|
|
61
|
+
darker edge on hover for interactive controls
|
|
62
|
+
|
|
63
|
+
## Sizing — the `size` axis (`sm` / `md` / `lg`)
|
|
64
|
+
|
|
65
|
+
### Control heights
|
|
66
|
+
|
|
67
|
+
- `--ui-height-sm` — `1.5rem` / 24px (`sm`)
|
|
68
|
+
- `--ui-height` — `2rem` / 32px (`md`, **default**)
|
|
69
|
+
- `--ui-height-lg` — `2.5rem` / 40px (`lg`)
|
|
70
|
+
- `--ui-height-touch` — `2.75rem` / 44px — explicit touch target (WCAG/Apple min), **not** the `lg` size
|
|
71
|
+
|
|
72
|
+
### Text
|
|
73
|
+
|
|
74
|
+
- `--ui-text-sm` — `0.75rem` / 12px · `--ui-text` — `0.875rem` / 14px (default) · `--ui-text-lg` — `1rem` / 16px
|
|
75
|
+
|
|
76
|
+
### Inline icons
|
|
77
|
+
|
|
78
|
+
- `--ui-icon-sm` — `0.875rem` / 14px · `--ui-icon` — `1rem` / 16px (default) · `--ui-icon-lg` — `1.25rem` / 20px
|
|
79
|
+
|
|
80
|
+
> Per-component **padding / gap** is NOT a token — each component maps the `size` prop to Tailwind
|
|
81
|
+
> class maps (`Record<Size, string>`) internally. (The old `--ui-padding*` and `--ui-border` width
|
|
82
|
+
> tokens were removed.)
|
|
83
|
+
|
|
84
|
+
## Breakpoints (in `@theme`)
|
|
85
|
+
|
|
86
|
+
Narrow-mobile tiers and an ultrawide tier on top of Tailwind's defaults, so `DataTable` column
|
|
87
|
+
placements can target the full range:
|
|
88
|
+
`4xs` 320px · `3xs` 360px · `2xs` 420px · `xs` 480px · `3xl` 1920px.
|
|
89
|
+
|
|
90
|
+
## Theme values
|
|
91
|
+
|
|
92
|
+
### Light (default)
|
|
93
|
+
|
|
94
|
+
```css
|
|
95
|
+
--ui-color-background: #ffffff;
|
|
96
|
+
--ui-color-text: #111827;
|
|
97
|
+
--ui-color-primary: #1d4ed8;
|
|
98
|
+
--ui-color-secondary: #0891b2;
|
|
99
|
+
--ui-color-success: #059669;
|
|
100
|
+
--ui-color-warning: #d97706;
|
|
101
|
+
--ui-color-error: #dc2626;
|
|
102
|
+
--ui-color-info: #0ea5e9;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Dark
|
|
106
|
+
|
|
107
|
+
```css
|
|
108
|
+
--ui-color-background: #111827;
|
|
109
|
+
--ui-color-text: #f9fafb;
|
|
110
|
+
--ui-color-primary: #2563eb;
|
|
111
|
+
--ui-color-secondary: #06b6d4;
|
|
112
|
+
--ui-color-success: #10b981;
|
|
113
|
+
--ui-color-warning: #f59e0b;
|
|
114
|
+
--ui-color-error: #ef4444;
|
|
115
|
+
--ui-color-info: #06b6d4;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The sizing/text/icon tokens are theme-independent. The interactive tints use the same percentages
|
|
119
|
+
in both themes — only `--ui-color-text` (hence `--ui-accent`) flips.
|
|
120
|
+
|
|
121
|
+
## Usage in components
|
|
122
|
+
|
|
123
|
+
Components reference these via Tailwind's arbitrary-value syntax:
|
|
124
|
+
|
|
125
|
+
```svelte
|
|
126
|
+
<!-- Text / background -->
|
|
127
|
+
<div class="text-[var(--ui-color-text)] bg-[var(--ui-color-background)]">...</div>
|
|
128
|
+
|
|
129
|
+
<!-- Border (color + radius tokens) -->
|
|
130
|
+
<div
|
|
131
|
+
class="border [border-color:var(--ui-border-color-strong)] [border-radius:var(--ui-border-radius)]"
|
|
132
|
+
>
|
|
133
|
+
...
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<!-- Hover / section / pressed surfaces — use the shared tints, not ad-hoc color-mix -->
|
|
137
|
+
<div
|
|
138
|
+
class="[background-color:var(--ui-color-surface)] hover:[background-color:var(--ui-color-hover)]"
|
|
139
|
+
>
|
|
140
|
+
...
|
|
141
|
+
</div>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
For muted text or faint dividers, mix the text color: `color-mix(in srgb, var(--ui-color-text) 60%, transparent)`.
|
|
145
|
+
|
|
146
|
+
## Theme application
|
|
147
|
+
|
|
148
|
+
Precedence:
|
|
149
|
+
|
|
150
|
+
1. Forced theme via `.theme-light` / `.theme-dark` — set on **`<html>`** by `ThemeSelector` and by
|
|
151
|
+
the pre-hydration script in `app.html`. A switcher **must** target `<html>` (documentElement),
|
|
152
|
+
never `<body>` — putting it on `<body>` leaves `<html>` on the system path and freezes the
|
|
153
|
+
accent-derived tints to the wrong theme.
|
|
154
|
+
2. System preference via `@media (prefers-color-scheme: dark)`.
|
|
155
|
+
3. Default light values.
|
|
156
|
+
|
|
157
|
+
`ThemeSelector` manages switching (Auto / Light / Dark) and persists the choice in `localStorage`
|
|
158
|
+
under `svelte-ui-theme`.
|
|
159
|
+
|
|
160
|
+
**Scoped / inverse surfaces:** because the theme classes are ordinary CSS, apply one to any
|
|
161
|
+
container to re-theme just that subtree — e.g. `class="theme-dark"` on a dark header sitting on a
|
|
162
|
+
light page, so the components inside resolve light-on-dark and stay visible. This works because the
|
|
163
|
+
tint tokens are re-defined per theme scope; see the themes skill.
|
|
164
|
+
|
|
165
|
+
## Building a custom theme
|
|
166
|
+
|
|
167
|
+
This page documents the token _surface_. For the authoring side — swapping palette/sizing, tinting
|
|
168
|
+
by variant (`--ui-accent` + `.ui-accent`), scoping/inverse surfaces, the custom-property freeze
|
|
169
|
+
rule, the self-reference trap, and bridging to another design system (DaisyUI, shadcn, …) — see the
|
|
170
|
+
themes skill: [`.claude/skills/themes/SKILL.md`](../.claude/skills/themes/SKILL.md).
|