@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.
@@ -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).