@skewedaspect/sleekspace-ui 0.4.0 → 0.5.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.
@@ -42,9 +42,29 @@ const { currentTheme, setTheme } = useTheme();
42
42
  </template>
43
43
  ```
44
44
 
45
+ ## SkPage as Theme Provider
46
+
47
+ If your app already uses `SkPage`, you can skip the `SkTheme` wrapper and pass a `theme` prop directly:
48
+
49
+ ```vue
50
+ <template>
51
+ <SkPage theme="colorful" fixed-header>
52
+ <template #header>
53
+ <SkNavBar kind="primary">
54
+ <template #brand>My App</template>
55
+ </SkNavBar>
56
+ </template>
57
+
58
+ <SkPanel kind="accent">Fully themed — no SkTheme wrapper needed.</SkPanel>
59
+ </SkPage>
60
+ </template>
61
+ ```
62
+
63
+ This establishes the same theme context that `SkTheme` does (including portal component support). The `useTheme` composable works identically inside a themed `SkPage`.
64
+
45
65
  ## How It Works
46
66
 
47
- SkTheme sets a `data-scheme` attribute on its wrapper element. CSS rules scoped to each scheme override semantic token values:
67
+ SkTheme (or SkPage with a `theme` prop) sets a `data-scheme` attribute on its wrapper element. This cascades the correct text color (`--sk-text-primary`) to all descendants. SkPage also sets the page background (`--sk-surface-base`). CSS rules scoped to each scheme override semantic token values:
48
68
 
49
69
  ```css
50
70
  [data-scheme="greyscale"] {
@@ -60,30 +80,274 @@ All components reference these semantic tokens, so switching the scheme instantl
60
80
 
61
81
  ## Creating Custom Themes
62
82
 
63
- Define a custom theme by providing CSS custom property overrides on a new `data-scheme` value:
83
+ Themes are pure CSS. You define a `[data-scheme="name"]` block with 35 custom properties (7 semantic kinds × 5 tokens each) and you're done. No library modifications, no build steps — just a stylesheet.
84
+
85
+ > See the [SkTheme component page](/components/theme) for live demos of custom themes compared side-by-side with the built-in ones.
86
+
87
+ ### Token Structure
88
+
89
+ Each of the 7 semantic kinds requires 5 tokens:
90
+
91
+ | Token | Purpose |
92
+ |-------|---------|
93
+ | `--sk-{kind}-base` | Main background color |
94
+ | `--sk-{kind}-hover` | Hover state background |
95
+ | `--sk-{kind}-active` | Active/pressed state background |
96
+ | `--sk-{kind}-text` | Text color on the base background |
97
+ | `--sk-{kind}-text-contrast` | Alternative text for subtle/ghost variants |
98
+
99
+ The 7 kinds are: `neutral`, `primary`, `accent`, `success`, `warning`, `danger`, `info`.
100
+
101
+ ### Surface Tokens (Optional)
102
+
103
+ Themes can also override the page surface tokens. These have sensible defaults (dark gray background, white text) so you only need to override them if your theme wants a different page feel:
104
+
105
+ | Token | Default | Purpose |
106
+ |-------|---------|---------|
107
+ | `--sk-surface-base` | `--sk-color-gray-90` | Page/app background |
108
+ | `--sk-surface-raised` | `--sk-color-gray-95` | Elevated surfaces (cards, panels) |
109
+ | `--sk-surface-overlay` | `--sk-color-gray-80` | Overlays (modals, dropdowns) |
110
+ | `--sk-text-primary` | white | Primary body text |
111
+
112
+ `SkPage` applies `--sk-surface-base` as its background automatically. If you use `SkTheme` without `SkPage`, set your body background to `var(--sk-surface-base)` for the dark page feel.
113
+
114
+ ### Deriving Hover and Active States
115
+
116
+ You don't need to hand-pick hover/active colors. The `color-mix()` function lightens the base color automatically, which is how the built-in themes do it:
64
117
 
65
118
  ```css
119
+ --sk-primary-hover: color-mix(
120
+ in oklch,
121
+ var(--sk-primary-base),
122
+ var(--sk-color-gray-10) var(--sk-mix-amount-moderate) /* 15% lighter */
123
+ );
124
+ --sk-primary-active: color-mix(
125
+ in oklch,
126
+ var(--sk-primary-base),
127
+ var(--sk-color-gray-10) var(--sk-mix-amount-strong) /* 20% lighter */
128
+ );
129
+ ```
130
+
131
+ This pattern references the `--sk-color-gray-10` foundation token (a light gray) and mixes it in at either 15% (`moderate`) or 20% (`strong`). You can also use hardcoded OKLCH values if you prefer full control.
132
+
133
+ ### Complete Example: "Ocean" Theme
134
+
135
+ Here's a full theme definition using OKLCH values. This theme uses deep blues for neutral elements, teal for primary, coral for accent, and keeps the standard semantic colors for success/warning/danger/info:
136
+
137
+ ```css
138
+ /* ocean-theme.css — import this after SleekSpace UI styles */
139
+
66
140
  [data-scheme="ocean"] {
67
- --sk-primary-base: oklch(0.55 0.15 230);
141
+ /* Neutral deep slate blue */
142
+ --sk-neutral-base: oklch(0.35 0.04 250);
143
+ --sk-neutral-hover: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
144
+ --sk-neutral-active: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
145
+ --sk-neutral-text: oklch(1 0 0);
146
+ --sk-neutral-text-contrast: oklch(0.15 0.02 250);
147
+
148
+ /* Primary — bright teal */
149
+ --sk-primary-base: oklch(0.72 0.15 195);
150
+ --sk-primary-hover: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
151
+ --sk-primary-active: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
68
152
  --sk-primary-text: oklch(1 0 0);
69
- --sk-primary-hover: oklch(0.45 0.15 230);
153
+ --sk-primary-text-contrast: oklch(0.15 0.02 195);
70
154
 
71
- --sk-accent-base: oklch(0.6 0.2 180);
155
+ /* Accent warm coral */
156
+ --sk-accent-base: oklch(0.68 0.18 25);
157
+ --sk-accent-hover: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
158
+ --sk-accent-active: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
72
159
  --sk-accent-text: oklch(1 0 0);
73
- --sk-accent-hover: oklch(0.5 0.2 180);
160
+ --sk-accent-text-contrast: oklch(0.15 0.02 25);
74
161
 
75
- /* Define all 7 semantic kinds: neutral, primary, accent, info, success, warning, danger */
162
+ /* Success emerald green */
163
+ --sk-success-base: oklch(0.70 0.18 155);
164
+ --sk-success-hover: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
165
+ --sk-success-active: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
166
+ --sk-success-text: oklch(1 0 0);
167
+ --sk-success-text-contrast: oklch(0.15 0.02 155);
168
+
169
+ /* Warning — amber */
170
+ --sk-warning-base: oklch(0.85 0.16 85);
171
+ --sk-warning-hover: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
172
+ --sk-warning-active: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
173
+ --sk-warning-text: oklch(1 0 0);
174
+ --sk-warning-text-contrast: oklch(0.15 0.02 85);
175
+
176
+ /* Danger — crimson red */
177
+ --sk-danger-base: oklch(0.60 0.22 25);
178
+ --sk-danger-hover: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
179
+ --sk-danger-active: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
180
+ --sk-danger-text: oklch(1 0 0);
181
+ --sk-danger-text-contrast: oklch(0.15 0.02 25);
182
+
183
+ /* Info — sky blue */
184
+ --sk-info-base: oklch(0.75 0.12 230);
185
+ --sk-info-hover: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
186
+ --sk-info-active: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
187
+ --sk-info-text: oklch(1 0 0);
188
+ --sk-info-text-contrast: oklch(0.15 0.02 230);
76
189
  }
77
190
  ```
78
191
 
79
- Then use it with the theme component:
192
+ ### Complete Example: "Cyberpunk" Theme
193
+
194
+ Neon pink primary, electric cyan accent, deep violet neutrals, and screaming neon green for success. High chroma across the board.
195
+
196
+ ```css
197
+ /* cyberpunk-theme.css */
198
+
199
+ [data-scheme="cyberpunk"] {
200
+ /* Neutral — deep violet */
201
+ --sk-neutral-base: oklch(0.25 0.08 300);
202
+ --sk-neutral-hover: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
203
+ --sk-neutral-active: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
204
+ --sk-neutral-text: oklch(1 0 0);
205
+ --sk-neutral-text-contrast: oklch(0.12 0.04 300);
206
+
207
+ /* Primary — hot pink */
208
+ --sk-primary-base: oklch(0.65 0.27 355);
209
+ --sk-primary-hover: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
210
+ --sk-primary-active: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
211
+ --sk-primary-text: oklch(1 0 0);
212
+ --sk-primary-text-contrast: oklch(0.12 0.04 355);
213
+
214
+ /* Accent — electric cyan */
215
+ --sk-accent-base: oklch(0.78 0.15 195);
216
+ --sk-accent-hover: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
217
+ --sk-accent-active: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
218
+ --sk-accent-text: oklch(1 0 0);
219
+ --sk-accent-text-contrast: oklch(0.12 0.04 195);
220
+
221
+ /* Success — neon green */
222
+ --sk-success-base: oklch(0.82 0.25 145);
223
+ --sk-success-hover: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
224
+ --sk-success-active: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
225
+ --sk-success-text: oklch(1 0 0);
226
+ --sk-success-text-contrast: oklch(0.12 0.04 145);
227
+
228
+ /* Warning — electric yellow */
229
+ --sk-warning-base: oklch(0.90 0.18 100);
230
+ --sk-warning-hover: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
231
+ --sk-warning-active: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
232
+ --sk-warning-text: oklch(1 0 0);
233
+ --sk-warning-text-contrast: oklch(0.12 0.04 100);
234
+
235
+ /* Danger — hot red */
236
+ --sk-danger-base: oklch(0.62 0.25 30);
237
+ --sk-danger-hover: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
238
+ --sk-danger-active: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
239
+ --sk-danger-text: oklch(1 0 0);
240
+ --sk-danger-text-contrast: oklch(0.12 0.04 30);
241
+
242
+ /* Info — neon purple */
243
+ --sk-info-base: oklch(0.60 0.25 305);
244
+ --sk-info-hover: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
245
+ --sk-info-active: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
246
+ --sk-info-text: oklch(1 0 0);
247
+ --sk-info-text-contrast: oklch(0.12 0.04 305);
248
+ }
249
+ ```
250
+
251
+ ### Using a Custom Theme
252
+
253
+ Apply it with `SkTheme` or `SkPage`:
80
254
 
81
255
  ```vue
256
+ <!-- With SkTheme -->
82
257
  <SkTheme theme="ocean">
83
258
  <YourContent />
84
259
  </SkTheme>
260
+
261
+ <!-- Or directly on SkPage -->
262
+ <SkPage theme="ocean" fixed-header>
263
+ <template #header>...</template>
264
+ <YourContent />
265
+ </SkPage>
266
+ ```
267
+
268
+ > **TypeScript note:** To get type-safe autocomplete for custom theme names, augment the `SkThemeNameMap` interface:
269
+ >
270
+ > ```ts
271
+ > declare module '@skewedaspect/sleekspace-ui' {
272
+ > interface SkThemeNameMap {
273
+ > ocean : true;
274
+ > }
275
+ > }
276
+ > ```
277
+ >
278
+ > This registers `'ocean'` as a valid `SkThemeName` throughout your project.
279
+
280
+ ### Using Foundation Colors
281
+
282
+ Instead of raw OKLCH values, you can reference the built-in foundation color palette. Each color family has 11 shades (05 through 95, where 50 is the most saturated):
283
+
284
+ ```css
285
+ [data-scheme="mytheme"] {
286
+ /* Reference foundation palette variables */
287
+ --sk-primary-base: var(--sk-color-purple-50);
288
+ --sk-accent-base: var(--sk-color-pink-50);
289
+ --sk-neutral-base: var(--sk-color-gray-60);
290
+
291
+ /* hover/active states auto-derived the same way */
292
+ --sk-primary-hover: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
293
+ /* ... */
294
+ }
295
+ ```
296
+
297
+ Available foundation color families: `gray`, `blue`, `red`, `orange`, `yellow`, `green`, `mint`, `cyan`, `purple`, `pink`.
298
+
299
+ ### Quick Reference: Minimal Theme
300
+
301
+ If you just want to remap which colors go where, copy this template and change the `-base` values. The `color-mix()` lines can be copied verbatim — they derive from the base automatically:
302
+
303
+ ```css
304
+ [data-scheme="mytheme"] {
305
+ --sk-neutral-base: /* your color */;
306
+ --sk-neutral-hover: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
307
+ --sk-neutral-active: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
308
+ --sk-neutral-text: oklch(1 0 0);
309
+ --sk-neutral-text-contrast: var(--sk-color-gray-95);
310
+
311
+ --sk-primary-base: /* your color */;
312
+ --sk-primary-hover: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
313
+ --sk-primary-active: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
314
+ --sk-primary-text: oklch(1 0 0);
315
+ --sk-primary-text-contrast: var(--sk-color-gray-95);
316
+
317
+ --sk-accent-base: /* your color */;
318
+ --sk-accent-hover: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
319
+ --sk-accent-active: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
320
+ --sk-accent-text: oklch(1 0 0);
321
+ --sk-accent-text-contrast: var(--sk-color-gray-95);
322
+
323
+ --sk-success-base: /* your color */;
324
+ --sk-success-hover: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
325
+ --sk-success-active: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
326
+ --sk-success-text: oklch(1 0 0);
327
+ --sk-success-text-contrast: var(--sk-color-gray-95);
328
+
329
+ --sk-warning-base: /* your color */;
330
+ --sk-warning-hover: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
331
+ --sk-warning-active: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
332
+ --sk-warning-text: oklch(1 0 0);
333
+ --sk-warning-text-contrast: var(--sk-color-gray-95);
334
+
335
+ --sk-danger-base: /* your color */;
336
+ --sk-danger-hover: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
337
+ --sk-danger-active: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
338
+ --sk-danger-text: oklch(1 0 0);
339
+ --sk-danger-text-contrast: var(--sk-color-gray-95);
340
+
341
+ --sk-info-base: /* your color */;
342
+ --sk-info-hover: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
343
+ --sk-info-active: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
344
+ --sk-info-text: oklch(1 0 0);
345
+ --sk-info-text-contrast: var(--sk-color-gray-95);
346
+ }
85
347
  ```
86
348
 
349
+ Only the 7 `-base` values need to change. Everything else is boilerplate that can be copied as-is.
350
+
87
351
  ## Semantic Kinds
88
352
 
89
353
  All themed components accept a `kind` prop with these values:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skewedaspect/sleekspace-ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A Vue 3 component library with a cyberpunk aesthetic, featuring OKLCH colors, beveled corners, and a powerful design token system",
5
5
  "type": "module",
6
6
  "main": "dist/sleekspace-ui.umd.js",
@@ -49,10 +49,10 @@
49
49
  "license": "MIT",
50
50
  "repository": {
51
51
  "type": "git",
52
- "url": "git+https://gitlab.com/skewedaspect/sleekspace-ui.git",
52
+ "url": "git+https://gitlab.com/skewed-aspect/sleekspace-ui.git",
53
53
  "directory": "packages/sleekspace-ui"
54
54
  },
55
- "bugs": "https://gitlab.com/skewedaspect/sleekspace-ui/-/issues",
55
+ "bugs": "https://gitlab.com/skewed-aspect/sleekspace-ui/-/issues",
56
56
  "homepage": "https://sleekspace.skewedaspect.com",
57
57
  "peerDependencies": {
58
58
  "vue": "^3.3.0"
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="classes" :style="customStyles">
2
+ <div :class="classes" :style="customStyles" :data-scheme="theme">
3
3
  <header v-if="$slots.header" class="sk-page-header">
4
4
  <slot name="header" />
5
5
  </header>
@@ -62,11 +62,15 @@
62
62
  * width below the sidebar and content areas.
63
63
  */
64
64
 
65
- import { computed } from 'vue';
65
+ import { computed, provide, watch } from 'vue';
66
66
 
67
67
  // Types
68
+ import type { SkThemeName } from '../Theme/types';
68
69
  import type { SkPageSidebarPosition } from './types';
69
70
 
71
+ // Composables
72
+ import { provideTheme } from '../Theme/useTheme';
73
+
70
74
  //------------------------------------------------------------------------------------------------------------------
71
75
 
72
76
  export interface SkPageComponentProps
@@ -101,6 +105,14 @@
101
105
  * Only applies when the sidebar slot is provided.
102
106
  */
103
107
  sidebarWidth ?: string;
108
+
109
+ /**
110
+ * Optional theme name. When provided, SkPage acts as a theme provider — setting
111
+ * `data-scheme` on the root element and providing theme context for descendant
112
+ * components (including portal components like dropdowns and modals).
113
+ * When omitted, SkPage has no theme behavior.
114
+ */
115
+ theme ?: SkThemeName;
104
116
  }
105
117
 
106
118
  //------------------------------------------------------------------------------------------------------------------
@@ -110,6 +122,7 @@
110
122
  fixedHeader: false,
111
123
  fixedFooter: false,
112
124
  sidebarWidth: undefined,
125
+ theme: undefined,
113
126
  });
114
127
 
115
128
  //------------------------------------------------------------------------------------------------------------------
@@ -138,6 +151,27 @@
138
151
  '--sk-page-sidebar-width': props.sidebarWidth,
139
152
  };
140
153
  });
154
+
155
+ //------------------------------------------------------------------------------------------------------------------
156
+ // Theme Provider
157
+ //------------------------------------------------------------------------------------------------------------------
158
+
159
+ if(props.theme)
160
+ {
161
+ const { currentTheme, setTheme } = provideTheme(props.theme);
162
+
163
+ // Provide theme for portal components (dropdown, modal, tooltip, etc.)
164
+ provide('sk-theme', currentTheme);
165
+
166
+ // Watch for external theme prop changes
167
+ watch(() => props.theme, (newTheme) =>
168
+ {
169
+ if(newTheme)
170
+ {
171
+ setTheme(newTheme);
172
+ }
173
+ });
174
+ }
141
175
  </script>
142
176
 
143
177
  <!--------------------------------------------------------------------------------------------------------------------->
@@ -2,6 +2,10 @@
2
2
  // Page Types
3
3
  //----------------------------------------------------------------------------------------------------------------------
4
4
 
5
+ import type { SkThemeName } from '../Theme/types';
6
+
7
+ //----------------------------------------------------------------------------------------------------------------------
8
+
5
9
  /** Sidebar position */
6
10
  export type SkPageSidebarPosition = 'left' | 'right';
7
11
 
@@ -16,6 +20,8 @@ export interface SkPageProps
16
20
  fixedFooter ?: boolean;
17
21
  /** Custom sidebar width */
18
22
  sidebarWidth ?: string;
23
+ /** Optional theme name — when provided, SkPage acts as a theme provider */
24
+ theme ?: SkThemeName;
19
25
  }
20
26
 
21
27
  //----------------------------------------------------------------------------------------------------------------------
@@ -2,12 +2,29 @@
2
2
  // Theme Component Types
3
3
  //----------------------------------------------------------------------------------------------------------------------
4
4
 
5
+ /**
6
+ * Registry of available theme names. Augment this interface to register custom themes:
7
+ *
8
+ * ```ts
9
+ * declare module '@skewedaspect/sleekspace-ui' {
10
+ * interface SkThemeNameMap {
11
+ * ocean : true;
12
+ * midnight : true;
13
+ * }
14
+ * }
15
+ * ```
16
+ */
17
+ export interface SkThemeNameMap
18
+ {
19
+ greyscale : true;
20
+ colorful : true;
21
+ }
22
+
5
23
  /**
6
24
  * Available theme names that control the overall color scheme of the application.
7
- * - 'greyscale': A monochromatic theme with neutral grays and subtle accents
8
- * - 'colorful': A vibrant theme with saturated colors and stronger contrasts
25
+ * Derived from {@link SkThemeNameMap} augment that interface to add custom themes.
9
26
  */
10
- export type SkThemeName = 'greyscale' | 'colorful';
27
+ export type SkThemeName = keyof SkThemeNameMap;
11
28
 
12
29
  //----------------------------------------------------------------------------------------------------------------------
13
30
 
@@ -56,8 +56,8 @@
56
56
 
57
57
  /* Smooth transitions for interactive states */
58
58
  transition:
59
- background-color var(--sk-transition-duration-fast) var(--sk-transition-easing-standard),
60
- border-color var(--sk-transition-duration-fast) var(--sk-transition-easing-standard);
59
+ background-color var(--sk-transition-duration-fast) var(--sk-transition-easing-ease-out),
60
+ border-color var(--sk-transition-duration-fast) var(--sk-transition-easing-ease-out);
61
61
  }
62
62
 
63
63
  /* Hover State */
@@ -21,7 +21,7 @@ body
21
21
  {
22
22
  margin: 0;
23
23
  min-height: 100svh;
24
- color: var(--sk-neutral-fg);
24
+ color: var(--sk-text-primary);
25
25
  }
26
26
 
27
27
  img,
@@ -40,8 +40,8 @@ canvas
40
40
 
41
41
  :focus-visible
42
42
  {
43
- outline: var(--sk-focus-outline-width) solid currentColor;
44
- outline-offset: var(--sk-focus-outline-offset);
43
+ outline: var(--sk-focus-ring-width) solid currentColor;
44
+ outline-offset: var(--sk-focus-ring-offset);
45
45
  }
46
46
 
47
47
  //----------------------------------------------------------------------------------------------------------------------
@@ -103,10 +103,10 @@
103
103
  --sk-button-outline-bg: rgba(5, 5, 5, 0.15);
104
104
 
105
105
  /// Subtle variant background opacity (references global foundation token)
106
- --sk-button-subtle-opacity: var(--sk-opacity-subtle);
106
+ --sk-button-subtle-opacity: 0.35;
107
107
 
108
108
  /// Subtle variant hover background opacity (references global foundation token)
109
- --sk-button-subtle-opacity-hover: var(--sk-opacity-subtle-hover);
109
+ --sk-button-subtle-opacity-hover: 0.65;
110
110
 
111
111
  /// Subtle variant border opacity (references global foundation token)
112
112
  --sk-button-subtle-border-opacity: var(--sk-opacity-subtle-border);
@@ -260,10 +260,9 @@
260
260
 
261
261
  &:hover:not(:disabled)
262
262
  {
263
- --sk-button-border-color: oklch(from var(--sk-button-border-base) l c h / var(--sk-button-subtle-border-opacity-hover));
264
263
  --sk-button-glow-shadow: 0 0 var(--sk-button-glow-size) oklch(from var(--sk-button-border-base) l c h / var(--sk-button-glow-opacity));
265
264
 
266
- background-color: oklch(from var(--sk-button-bg) l c h / var(--sk-button-subtle-opacity-hover));
265
+ background-color: var(--sk-button-bg-hover);
267
266
  }
268
267
 
269
268
  &:active:not(:disabled)
@@ -534,6 +533,12 @@
534
533
  @include button-subtle-kind($kind);
535
534
  }
536
535
 
536
+ &:disabled,
537
+ &.sk-loading
538
+ {
539
+ background-color: oklch(from var(--sk-button-bg) l c h / 0.45);
540
+ }
541
+
537
542
  // Special case: neutral text and border are lighter (halfway to white) for better differentiation
538
543
  &.sk-neutral
539
544
  {
@@ -624,20 +629,51 @@
624
629
 
625
630
  &.sk-pressed
626
631
  {
627
- &.sk-outline,
628
- &.sk-ghost
632
+ &.sk-solid
629
633
  {
630
- background-color: oklch(from var(--sk-button-bg) l c h / var(--sk-button-subtle-opacity));
634
+ --sk-button-border-color: var(--sk-button-border-hover);
635
+ background-color: var(--sk-button-bg-hover);
636
+ color: var(--sk-button-text);
637
+
638
+ &:hover:not(:disabled)
639
+ {
640
+ --sk-button-glow-shadow: 0 0 var(--sk-button-glow-size) oklch(from var(--sk-button-border-base) l c h / var(--sk-button-glow-opacity));
641
+ background-color: color-mix(in oklch, var(--sk-button-bg) 70%, white 30%);
642
+ }
631
643
  }
632
644
 
633
- &.sk-solid
645
+ &.sk-outline
634
646
  {
635
- background-color: var(--sk-button-bg-active);
647
+ --sk-button-border-color: var(--sk-button-border-hover);
648
+ background-color: var(--sk-button-bg);
649
+ color: var(--sk-button-text);
636
650
  }
637
651
 
638
652
  &.sk-subtle
639
653
  {
654
+ --sk-button-border-color: transparent;
640
655
  background-color: var(--sk-button-bg);
656
+ color: var(--sk-button-text);
657
+
658
+ &:hover:not(:disabled)
659
+ {
660
+ --sk-button-border-color: transparent;
661
+ --sk-button-glow-shadow: 0 0 var(--sk-button-glow-size) oklch(from var(--sk-button-border-base) l c h / var(--sk-button-glow-opacity));
662
+ background-color: var(--sk-button-bg-hover);
663
+ }
664
+ }
665
+
666
+ &.sk-ghost
667
+ {
668
+ --sk-button-border-color: color-mix(in oklch, var(--sk-button-border-base) 50%, transparent 50%);
669
+ border-color: color-mix(in oklch, var(--sk-button-border-base) 50%, transparent 50%);
670
+ background-color: var(--sk-button-outline-bg);
671
+ color: var(--sk-button-text);
672
+ }
673
+
674
+ &.sk-link
675
+ {
676
+ color: var(--sk-button-text);
641
677
  }
642
678
  }
643
679
 
@@ -12,8 +12,8 @@
12
12
  display: flex;
13
13
  flex-direction: column;
14
14
  min-height: 100vh;
15
- background: var(--sk-background);
16
- color: var(--sk-foreground);
15
+ background: var(--sk-surface-base);
16
+ color: var(--sk-text-primary);
17
17
 
18
18
  //------------------------------------------------------------------------------------------------------------------
19
19
  // Header
@@ -83,10 +83,14 @@ To create a new theme:
83
83
  @forward 'mytheme'; // Add your theme
84
84
  ```
85
85
 
86
- 4. **Add the TypeScript type** in `src/components/Theme/types.ts`:
86
+ 4. **Register the TypeScript type** via module augmentation (e.g. in a `themes.d.ts` or your app's entry file):
87
87
 
88
88
  ```typescript
89
- export type SkThemeName = 'greyscale' | 'colorful' | 'mytheme';
89
+ declare module '@skewedaspect/sleekspace-ui' {
90
+ interface SkThemeNameMap {
91
+ mytheme : true;
92
+ }
93
+ }
90
94
  ```
91
95
 
92
96
  5. **Use your theme**:
@@ -156,6 +156,10 @@
156
156
  --sk-opacity-strong: 0.60;
157
157
  --sk-opacity-opaque: 1;
158
158
 
159
+ /* Semantic opacity aliases */
160
+ --sk-opacity-disabled: 0.5;
161
+ --sk-opacity-loading: 0.6;
162
+
159
163
  /* ===================================================================
160
164
  * Color Mixing Percentages
161
165
  * Tokenized mixing amounts for color-mix() function
@@ -26,6 +26,14 @@
26
26
  --sk-transition-easing-ease-out: ease-out;
27
27
  --sk-transition-easing-ease-in-out: ease-in-out;
28
28
 
29
+ /* ===================================================================
30
+ * Default Aliases
31
+ * Generic names that resolve to the most common values
32
+ * =================================================================== */
33
+
34
+ --sk-transition-duration-base: var(--sk-transition-duration-normal);
35
+ --sk-transition-easing: var(--sk-transition-easing-ease-out);
36
+
29
37
  /* ===================================================================
30
38
  * Common Transition Combinations
31
39
  * Convenience tokens for frequently used transitions
@@ -3,6 +3,10 @@
3
3
  *
4
4
  * Background and text colors for surfaces and general use.
5
5
  * These provide a consistent color system independent of component kinds.
6
+ *
7
+ * Defaults are defined on :root as fallbacks. Themes can override any of these
8
+ * via [data-scheme] selectors. The [data-scheme] rule also applies background
9
+ * and text color so that any themed element gets the correct surface treatment.
6
10
  */
7
11
 
8
12
  :root
@@ -34,3 +38,14 @@
34
38
  --sk-border-normal: var(--sk-color-gray-70); /* Standard borders */
35
39
  --sk-border-subtle: var(--sk-color-gray-80); /* Subtle borders/dividers */
36
40
  }
41
+
42
+ /**
43
+ * Apply text color to any themed element so descendants inherit the correct
44
+ * color without needing to set it themselves. Background is intentionally NOT
45
+ * set here — page-level containers like SkPage handle their own background
46
+ * via the surface tokens.
47
+ */
48
+ [data-scheme]
49
+ {
50
+ color: var(--sk-text-primary);
51
+ }