@skewedaspect/sleekspace-ui 0.3.0 → 0.5.0

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,264 @@ 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:** The `theme` prop is typed as `SkThemeName` which is `'greyscale' | 'colorful'`. For custom theme names, cast the value: `:theme="'ocean' as any"`. If you're building a library that adds themes, you can augment the type via module declaration.
269
+
270
+ ### Using Foundation Colors
271
+
272
+ 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):
273
+
274
+ ```css
275
+ [data-scheme="mytheme"] {
276
+ /* Reference foundation palette variables */
277
+ --sk-primary-base: var(--sk-color-purple-50);
278
+ --sk-accent-base: var(--sk-color-pink-50);
279
+ --sk-neutral-base: var(--sk-color-gray-60);
280
+
281
+ /* hover/active states auto-derived the same way */
282
+ --sk-primary-hover: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
283
+ /* ... */
284
+ }
285
+ ```
286
+
287
+ Available foundation color families: `gray`, `blue`, `red`, `orange`, `yellow`, `green`, `mint`, `cyan`, `purple`, `pink`.
288
+
289
+ ### Quick Reference: Minimal Theme
290
+
291
+ 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:
292
+
293
+ ```css
294
+ [data-scheme="mytheme"] {
295
+ --sk-neutral-base: /* your color */;
296
+ --sk-neutral-hover: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
297
+ --sk-neutral-active: color-mix(in oklch, var(--sk-neutral-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
298
+ --sk-neutral-text: oklch(1 0 0);
299
+ --sk-neutral-text-contrast: var(--sk-color-gray-95);
300
+
301
+ --sk-primary-base: /* your color */;
302
+ --sk-primary-hover: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
303
+ --sk-primary-active: color-mix(in oklch, var(--sk-primary-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
304
+ --sk-primary-text: oklch(1 0 0);
305
+ --sk-primary-text-contrast: var(--sk-color-gray-95);
306
+
307
+ --sk-accent-base: /* your color */;
308
+ --sk-accent-hover: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
309
+ --sk-accent-active: color-mix(in oklch, var(--sk-accent-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
310
+ --sk-accent-text: oklch(1 0 0);
311
+ --sk-accent-text-contrast: var(--sk-color-gray-95);
312
+
313
+ --sk-success-base: /* your color */;
314
+ --sk-success-hover: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
315
+ --sk-success-active: color-mix(in oklch, var(--sk-success-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
316
+ --sk-success-text: oklch(1 0 0);
317
+ --sk-success-text-contrast: var(--sk-color-gray-95);
318
+
319
+ --sk-warning-base: /* your color */;
320
+ --sk-warning-hover: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
321
+ --sk-warning-active: color-mix(in oklch, var(--sk-warning-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
322
+ --sk-warning-text: oklch(1 0 0);
323
+ --sk-warning-text-contrast: var(--sk-color-gray-95);
324
+
325
+ --sk-danger-base: /* your color */;
326
+ --sk-danger-hover: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
327
+ --sk-danger-active: color-mix(in oklch, var(--sk-danger-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
328
+ --sk-danger-text: oklch(1 0 0);
329
+ --sk-danger-text-contrast: var(--sk-color-gray-95);
330
+
331
+ --sk-info-base: /* your color */;
332
+ --sk-info-hover: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-moderate));
333
+ --sk-info-active: color-mix(in oklch, var(--sk-info-base), var(--sk-color-gray-10) var(--sk-mix-amount-strong));
334
+ --sk-info-text: oklch(1 0 0);
335
+ --sk-info-text-contrast: var(--sk-color-gray-95);
336
+ }
85
337
  ```
86
338
 
339
+ Only the 7 `-base` values need to change. Everything else is boilerplate that can be copied as-is.
340
+
87
341
  ## Semantic Kinds
88
342
 
89
343
  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.3.0",
3
+ "version": "0.5.0",
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,11 +49,11 @@
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",
56
- "homepage": "https://gitlab.com/skewedaspect/sleekspace-ui",
55
+ "bugs": "https://gitlab.com/skewed-aspect/sleekspace-ui/-/issues",
56
+ "homepage": "https://sleekspace.skewedaspect.com",
57
57
  "peerDependencies": {
58
58
  "vue": "^3.3.0"
59
59
  },
@@ -4,9 +4,9 @@
4
4
 
5
5
  <template>
6
6
  <div :class="classes" :style="customColorStyles" role="alert">
7
- <div class="sk-alert-icon">
7
+ <div v-if="shouldShowIcon" class="sk-alert-icon">
8
8
  <slot name="icon">
9
- <!-- Default icons for each kind -->
9
+ <!-- Default icons for feedback kinds -->
10
10
  <svg
11
11
  v-if="kind === 'info'"
12
12
  viewBox="0 0 24 24"
@@ -66,7 +66,7 @@
66
66
  /**
67
67
  * @component SkAlert
68
68
  * @description A contextual feedback component for displaying informational, success, warning, or error messages.
69
- * Features automatic icon selection based on alert kind and supports both subtle and prominent visual styles.
69
+ * Features automatic icon selection based on alert kind and supports prominent (default) and subtle visual styles.
70
70
  * Use alerts to communicate important status updates, validation results, or system messages to users.
71
71
  *
72
72
  * @example
@@ -74,11 +74,10 @@
74
74
  * <SkAlert kind="success">Your changes have been saved!</SkAlert>
75
75
  * ```
76
76
  *
77
- * @example Prominent warning with custom icon
77
+ * @example Subtle info alert
78
78
  * ```vue
79
- * <SkAlert kind="warning" prominent>
80
- * <template #icon><CustomIcon /></template>
81
- * This action cannot be undone.
79
+ * <SkAlert kind="info" subtle>
80
+ * A helpful tip for the user.
82
81
  * </SkAlert>
83
82
  * ```
84
83
  *
@@ -86,7 +85,7 @@
86
85
  * @slot icon - Custom icon to override the default kind-based icon. Receives no slot props.
87
86
  */
88
87
 
89
- import { computed, toRef } from 'vue';
88
+ import { computed, toRef, useSlots } from 'vue';
90
89
 
91
90
  // Types
92
91
  import type { SkAlertKind } from './types';
@@ -97,32 +96,50 @@
97
96
 
98
97
  //------------------------------------------------------------------------------------------------------------------
99
98
 
99
+ // Kinds that have default icons
100
+ const FEEDBACK_KINDS = [ 'info', 'success', 'warning', 'danger' ] as const;
101
+
102
+ //------------------------------------------------------------------------------------------------------------------
103
+
100
104
  export interface SkAlertComponentProps extends ComponentCustomColors
101
105
  {
102
106
  /**
103
- * Semantic kind that determines the alert's color scheme and default icon. Each kind
104
- * conveys a different meaning: 'info' for general information, 'success' for positive
105
- * confirmations, 'warning' for cautionary messages, and 'danger' for errors or critical issues.
107
+ * Semantic kind that determines the alert's color scheme and default icon. Supports all
108
+ * semantic kinds (neutral, primary, accent, info, success, warning, danger) as well as
109
+ * direct color kinds (neon-blue, neon-purple, etc.). Default icons are provided for the
110
+ * four feedback kinds (info, success, warning, danger).
106
111
  * @default 'info'
107
112
  */
108
113
  kind ?: SkAlertKind;
109
114
 
110
115
  /**
111
- * When true, applies prominent styling with a more vivid background color and stronger
112
- * visual presence. Use prominent alerts for critical messages that require immediate
113
- * user attention, such as errors or important warnings.
116
+ * When true, applies subdued styling with a lighter, semi-transparent background.
117
+ * Use subtle alerts for less critical messages that shouldn't dominate the visual
118
+ * hierarchy, such as tips or supplementary information.
114
119
  * @default false
115
120
  */
116
- prominent ?: boolean;
121
+ subtle ?: boolean;
122
+
123
+ /**
124
+ * Controls visibility of the icon container. When undefined (default), the icon is shown
125
+ * automatically if there's a default icon for the kind (info, success, warning, danger)
126
+ * or if custom content is provided via the icon slot. Set to false to force-hide the icon,
127
+ * or true to force-show the container even when empty.
128
+ * @default undefined
129
+ */
130
+ showIcon ?: boolean;
117
131
  }
118
132
 
119
133
  //------------------------------------------------------------------------------------------------------------------
120
134
 
121
135
  const props = withDefaults(defineProps<SkAlertComponentProps>(), {
122
136
  kind: 'info',
123
- prominent: false,
137
+ subtle: false,
138
+ showIcon: undefined,
124
139
  });
125
140
 
141
+ const slots = useSlots();
142
+
126
143
  //------------------------------------------------------------------------------------------------------------------
127
144
 
128
145
  const classes = computed(() =>
@@ -130,10 +147,26 @@
130
147
  return {
131
148
  'sk-alert': true,
132
149
  [`sk-${ props.kind }`]: true,
133
- 'sk-prominent': props.prominent,
150
+ 'sk-subtle': props.subtle,
134
151
  };
135
152
  });
136
153
 
154
+ // Determine if we should show the icon container
155
+ const shouldShowIcon = computed(() =>
156
+ {
157
+ // Explicit override takes precedence
158
+ if(props.showIcon !== undefined)
159
+ {
160
+ return props.showIcon;
161
+ }
162
+
163
+ // Auto-show if kind has a default icon or if slot content is provided
164
+ const hasDefaultIcon = FEEDBACK_KINDS.includes(props.kind as typeof FEEDBACK_KINDS[number]);
165
+ const hasSlotContent = !!slots.icon;
166
+
167
+ return hasDefaultIcon || hasSlotContent;
168
+ });
169
+
137
170
  //------------------------------------------------------------------------------------------------------------------
138
171
 
139
172
  // Custom color styles
@@ -2,9 +2,14 @@
2
2
  // Alert Component Types
3
3
  //----------------------------------------------------------------------------------------------------------------------
4
4
 
5
+ // Types
6
+ import type { ComponentKind } from '@/types';
7
+
8
+ //----------------------------------------------------------------------------------------------------------------------
9
+
5
10
  /**
6
- * Alert semantic kinds (feedback-focused subset)
11
+ * Alert kind - supports all semantic and color kinds
7
12
  */
8
- export type SkAlertKind = 'info' | 'success' | 'warning' | 'danger';
13
+ export type SkAlertKind = ComponentKind;
9
14
 
10
15
  //----------------------------------------------------------------------------------------------------------------------
@@ -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
  //----------------------------------------------------------------------------------------------------------------------
@@ -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
  //----------------------------------------------------------------------------------------------------------------------
@@ -37,7 +37,7 @@
37
37
  /// Glow effect opacity
38
38
  --sk-alert-glow-opacity: 0.3;
39
39
 
40
- /// Subtle background opacity (for non-prominent alerts)
40
+ /// Subtle background opacity (for default non-prominent alerts)
41
41
  --sk-alert-subtle-opacity: var(--sk-opacity-subtle);
42
42
 
43
43
  //------------------------------------------------------------------------------------------------------------------
@@ -107,8 +107,8 @@
107
107
  color: var(--sk-alert-fg);
108
108
  border-color: var(--sk-alert-border-color);
109
109
 
110
- // Prominent variants get the dark foreground color
111
- &.sk-prominent
110
+ // Non-subtle variants get the dark foreground color
111
+ &:not(.sk-subtle)
112
112
  {
113
113
  --sk-alert-fg: var(--sk-#{ $kind }-text);
114
114
  }
@@ -185,10 +185,10 @@
185
185
  }
186
186
 
187
187
  //------------------------------------------------------------------------------------------------------------------
188
- // Prominent variant
188
+ // Non-subtle (prominent) variant - applied when NOT subtle
189
189
  //------------------------------------------------------------------------------------------------------------------
190
190
 
191
- &.sk-prominent
191
+ &:not(.sk-subtle)
192
192
  {
193
193
  --sk-alert-padding: 1rem;
194
194
  --sk-alert-icon-size: 1.5rem;
@@ -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
@@ -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