@rimelight/ui 0.0.27 → 0.0.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimelight/ui",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "private": false,
5
5
  "description": "Rimelight Entertainment UI Library.",
6
6
  "keywords": [
@@ -2,10 +2,14 @@
2
2
  import { scv, cx } from "css-variants"
3
3
  import { useUi, resolveClasses } from "../../utils"
4
4
  import type { ComponentVariants, ComponentSlots } from "../../types"
5
- import buttonTheme from "../../themes/button.theme"
5
+ import buttonThemeDefault, { createButtonTheme } from "../../themes/button.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ const uiConfig = getUIConfig()
9
+ const buttonTheme = uiConfig.colors ? createButtonTheme(uiConfig.colors) : buttonThemeDefault
6
10
 
7
11
  const button = scv(buttonTheme)
8
- type ButtonVariants = ComponentVariants<typeof buttonTheme>
12
+ type ButtonVariants = ComponentVariants<typeof buttonThemeDefault>
9
13
 
10
14
  export interface RLAButtonProps {
11
15
  variant?: ButtonVariants['variant'];
@@ -1,16 +1,17 @@
1
1
  ---
2
- const PLACEHOLDER_THUMBNAIL = "/assets/blog-placeholder-1.jpg";
3
-
2
+ import { scv } from "css-variants"
3
+ import { useUi, resolveClasses } from "../../utils"
4
+ import type { ComponentVariants, ComponentSlots } from "../../types"
5
+ import embedYoutubeTheme from "../../themes/embedYoutube.theme"
6
+ import type { RLAIconProps } from "./RLAIcon.astro"
7
+ import { Image } from "astro:assets"
8
+
9
+ const PLACEHOLDER_THUMBNAIL = "/assets/placeholder.webp";
4
10
  const YOUTUBE_EMBED_BASE = "https://www.youtube.com/embed/";
5
-
6
11
  const VALID_ID_PATTERN = /^[\w-]{11}$/;
7
-
8
12
  const KNOWN_PREFIXES = ["www.", "m.", "music.", "gaming."];
9
-
10
13
  const YOUTUBE_THUMB_BASE = "https://i3.ytimg.com/vi/";
11
-
12
14
  const MAXRES_THUMBNAIL = "maxresdefault.jpg";
13
-
14
15
  const DEFAULT_THUMBNAIL = "hqdefault.jpg";
15
16
 
16
17
  const thumbnailAvailabilityCache = new Map<string, boolean>();
@@ -109,44 +110,75 @@ function getYoutubeEmbedUrl(rawUrl: string): string | null {
109
110
  return id ? `${YOUTUBE_EMBED_BASE}${id}?autoplay=1` : null;
110
111
  }
111
112
 
112
- interface Props {
113
+ const embedYoutube = scv(embedYoutubeTheme)
114
+ type EmbedYoutubeVariants = ComponentVariants<typeof embedYoutubeTheme>
115
+
116
+ export interface RLAEmbedYoutubeProps {
117
+ /**
118
+ * The URL of the YouTube video to embed.
119
+ */
113
120
  src: string;
121
+ /**
122
+ * The title of the video to display.
123
+ * @default "YouTube Video Player"
124
+ */
114
125
  title?: string;
115
- class?: string;
126
+ /**
127
+ * The name of the icon to display in the play trigger overlay.
128
+ * @default "i-lucide-play"
129
+ */
130
+ icon?: RLAIconProps["name"];
131
+ ui?: ComponentSlots<typeof embedYoutubeTheme>;
132
+ class?: any;
133
+ [key: string]: unknown;
116
134
  }
117
135
 
118
- const { src, title = "YouTube video player", class: className } = Astro.props;
136
+ const {
137
+ src,
138
+ title = "YouTube Video Player",
139
+ icon = "i-lucide-play",
140
+ ui: uiProp,
141
+ class: className,
142
+ ...rest
143
+ } = Astro.props as RLAEmbedYoutubeProps;
144
+
145
+ const classes = resolveClasses(embedYoutube, {
146
+ title: !!(Astro.slots.has("title") || Astro.props.title),
147
+ icon: !!(Astro.slots.has("icon") || Astro.props.icon),
148
+ }, useUi("embedYoutube", uiProp), className);
119
149
 
120
150
  const embedUrl = getYoutubeEmbedUrl(src);
121
151
  const thumbnail = await getYoutubeThumbnail(src);
122
152
  const isPlayable = Boolean(embedUrl);
123
- const ariaLabel = isPlayable ? "Play YouTube video" : "YouTube video unavailable";
153
+ const ariaLabel = isPlayable ? "Play YouTube Video" : "YouTube Video Unavailable";
124
154
  ---
125
155
 
126
- <div class:list={["relative aspect-video h-full w-full cursor-pointer overflow-hidden", className]} data-youtube-video>
156
+ <div
157
+ class:list={[classes.root]}
158
+ {...rest}
159
+ >
127
160
  <button
128
161
  type="button"
129
- class="absolute inset-0 aspect-video h-full w-full overflow-hidden group"
130
162
  data-youtube-trigger
131
163
  aria-label={ariaLabel}
132
164
  data-embed-url={isPlayable ? embedUrl : undefined}
133
165
  disabled={!isPlayable}
134
166
  data-video-title={title}
167
+ class:list={[classes.trigger]}
135
168
  >
136
- <img
169
+ <Image
170
+ layout="full-width"
137
171
  src={thumbnail}
138
- class="h-full w-full object-cover"
139
172
  alt={title}
140
173
  width="1280"
141
174
  height="720"
142
175
  loading="lazy"
176
+ class:list={[classes.image]}
143
177
  />
144
178
 
145
- <span class="pointer-events-none absolute inset-0 flex items-center justify-center bg-black/10 group-hover:bg-black/30 transition-colors">
146
- <svg class="h-20 w-20 text-white opacity-85 transition-all transform group-hover:opacity-100 group-hover:scale-110 duration-300" viewBox="0 0 24 24" fill="currentColor">
147
- <path d="M8 5v14l11-7z"/>
148
- </svg>
149
- </span>
179
+ <slot name="icon">
180
+ <span class:list={[classes.icon, icon]} aria-hidden="true" />
181
+ </slot>
150
182
  </button>
151
183
  </div>
152
184
 
@@ -167,7 +199,7 @@ const ariaLabel = isPlayable ? "Play YouTube video" : "YouTube video unavailable
167
199
  iframe.src = embedUrl;
168
200
  iframe.title = title;
169
201
  iframe.allow =
170
- "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
202
+ "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
171
203
  iframe.allowFullscreen = true;
172
204
  iframe.loading = "lazy";
173
205
  iframe.referrerPolicy = "strict-origin-when-cross-origin";
@@ -176,7 +208,6 @@ const ariaLabel = isPlayable ? "Play YouTube video" : "YouTube video unavailable
176
208
  root.replaceChildren(iframe);
177
209
  };
178
210
 
179
- // click and accessibility activation
180
211
  trigger.addEventListener("click", activate, { once: true });
181
212
  trigger.addEventListener("keydown", (event) => {
182
213
  if (event.key === "Enter" || event.key === " ") {
@@ -185,4 +216,4 @@ const ariaLabel = isPlayable ? "Play YouTube video" : "YouTube video unavailable
185
216
  }
186
217
  }, { once: true });
187
218
  })();
188
- </script>
219
+ </script>
@@ -104,23 +104,19 @@ const TitleTag = variant === "hero" ? "h1" : "h2";
104
104
  <slot name="top" />
105
105
 
106
106
  <div
107
- data-slot="container"
108
107
  class:list={[classes.container]}
109
108
  >
110
109
  {(Astro.slots.has("header") || Astro.props.icon || Astro.props.headline || Astro.props.title || Astro.props.description || Astro.slots.has("body") || Astro.slots.has("footer") || Astro.slots.has("links") || Astro.props.links?.length) && (
111
110
  <div
112
- data-slot="wrapper"
113
111
  class:list={[classes.wrapper]}
114
112
  >
115
113
  {(Astro.slots.has("header") || Astro.props.icon || Astro.props.headline || Astro.props.title || Astro.props.description) && (
116
114
  <div
117
- data-slot="header"
118
115
  class:list={[classes.header]}
119
116
  >
120
117
  <slot name="header">
121
118
  {(Astro.props.icon || Astro.slots.has("leading")) && (
122
119
  <div
123
- data-slot="leading"
124
120
  class:list={[classes.leading]}
125
121
  >
126
122
  <slot name="leading" {...{ ui: classes }}>
@@ -133,7 +129,6 @@ const TitleTag = variant === "hero" ? "h1" : "h2";
133
129
 
134
130
  {(Astro.props.headline || Astro.slots.has("headline")) && (
135
131
  <div
136
- data-slot="headline"
137
132
  class:list={[classes.headline]}
138
133
  >
139
134
  <slot name="headline">
@@ -144,7 +139,6 @@ const TitleTag = variant === "hero" ? "h1" : "h2";
144
139
 
145
140
  {(Astro.props.title || Astro.slots.has("title")) && (
146
141
  <TitleTag
147
- data-slot="title"
148
142
  class:list={[classes.title]}
149
143
  >
150
144
  <slot name="title">
@@ -155,7 +149,6 @@ const TitleTag = variant === "hero" ? "h1" : "h2";
155
149
 
156
150
  {(Astro.props.description || Astro.slots.has("description")) && (
157
151
  <div
158
- data-slot="description"
159
152
  class:list={[classes.description]}
160
153
  >
161
154
  <slot name="description">
@@ -169,7 +162,6 @@ const TitleTag = variant === "hero" ? "h1" : "h2";
169
162
 
170
163
  {Astro.slots.has("body") && (
171
164
  <div
172
- data-slot="body"
173
165
  class:list={[classes.body]}
174
166
  >
175
167
  <slot name="body" />
@@ -178,13 +170,11 @@ const TitleTag = variant === "hero" ? "h1" : "h2";
178
170
 
179
171
  {(Astro.slots.has("footer") || Astro.slots.has("links") || Astro.props.links?.length) && (
180
172
  <div
181
- data-slot="footer"
182
173
  class:list={[classes.footer]}
183
174
  >
184
175
  <slot name="footer">
185
176
  {(Astro.props.links?.length || Astro.slots.has("links")) && (
186
177
  <div
187
- data-slot="links"
188
178
  class:list={[classes.links]}
189
179
  >
190
180
  {Astro.props.links?.map((link) => (
package/src/env.d.ts CHANGED
@@ -9,7 +9,7 @@ declare module "virtual:nuxt-ui-plugins" {
9
9
  declare module "virtual:rimelight-ui" {
10
10
  import type { DefaultUIConfig } from "../themes"
11
11
 
12
- export function getUIConfig(): DefaultUIConfig
12
+ export function getUIConfig(): DefaultUIConfig & { colors?: string[] }
13
13
  }
14
14
 
15
15
  interface StarlightAddonsConfig {
@@ -33,6 +33,10 @@ export interface UIOptions {
33
33
  * wins).
34
34
  */
35
35
  ui?: UIConfigOverrides
36
+ /**
37
+ * Custom color names to dynamically generate variants for.
38
+ */
39
+ colors?: string[]
36
40
  }
37
41
 
38
42
  /**
@@ -59,7 +63,9 @@ export interface UIOptions {
59
63
  */
60
64
  export function ui(options: UIOptions = {}): AstroIntegration[] {
61
65
  const resolvedUI = defu(options.ui ?? {}, defaultUIConfig)
62
- const resolvedConfig = { ...resolvedUI, logos: options.logos ?? {} }
66
+ const coreColors = ["primary", "secondary", "info", "success", "warning", "error", "commentary", "ideation", "source"]
67
+ const mergedColors = [...new Set([...coreColors, ...(options.colors || [])])]
68
+ const resolvedConfig = { ...resolvedUI, logos: options.logos ?? {}, colors: mergedColors }
63
69
 
64
70
  const virtualModuleId = "virtual:rimelight-ui"
65
71
  const resolvedVirtualModuleId = "\0" + virtualModuleId
@@ -1,3 +1,5 @@
1
+ import { dirname, join } from "node:path"
2
+ import { fileURLToPath } from "node:url"
1
3
  import {
2
4
  definePreset,
3
5
  presetTypography,
@@ -7,9 +9,55 @@ import {
7
9
  transformerVariantGroup
8
10
  } from "unocss"
9
11
 
10
- export default definePreset(() => {
12
+ const __dirname = dirname(fileURLToPath(import.meta.url))
13
+ const uiSrcGlob = join(__dirname, "../**/*.{js,ts,jsx,tsx,vue,svelte,astro}")
14
+ const uiFilesystemGlob = join(__dirname, "../**/*")
15
+
16
+ const createScale = (colorName: string) => ({
17
+ DEFAULT: `var(--${colorName})`,
18
+ foreground: `var(--${colorName}-foreground)`,
19
+ accent: `var(--${colorName}-accent)`,
20
+ "50": `var(--color-${colorName}-50)`,
21
+ "100": `var(--color-${colorName}-100)`,
22
+ "200": `var(--color-${colorName}-200)`,
23
+ "300": `var(--color-${colorName}-300)`,
24
+ "400": `var(--color-${colorName}-400)`,
25
+ "500": `var(--color-${colorName}-500)`,
26
+ "600": `var(--color-${colorName}-600)`,
27
+ "700": `var(--color-${colorName}-700)`,
28
+ "800": `var(--color-${colorName}-800)`,
29
+ "900": `var(--color-${colorName}-900)`,
30
+ "950": `var(--color-${colorName}-950)`
31
+ })
32
+
33
+ export interface RimelightPresetOptions {
34
+ colors?: string[]
35
+ }
36
+
37
+ export default definePreset((options?: RimelightPresetOptions) => {
38
+ const customColors = options?.colors || []
39
+ const coreColors = ["primary", "secondary", "info", "success", "warning", "error", "commentary", "ideation", "source"]
40
+ const allColors = [...new Set([...coreColors, ...customColors])]
41
+
42
+ const safelist = allColors.flatMap(color => [
43
+ `bg-${color}-500`,
44
+ `hover:bg-${color}-600`,
45
+ `focus-visible:ring-${color}-500/50`,
46
+ `border-${color}-500/20`,
47
+ `text-${color}-600`,
48
+ `bg-${color}-500/5`,
49
+ `hover:bg-${color}-500/10`,
50
+ `hover:border-${color}-500/30`,
51
+ `focus-visible:ring-${color}-500/40`,
52
+ `bg-${color}-500/10`,
53
+ `hover:bg-${color}-500/20`,
54
+ `hover:underline`,
55
+ `hover:decoration-${color}-300`
56
+ ])
57
+
11
58
  return {
12
59
  name: "rimelight-ui",
60
+ safelist,
13
61
  presets: [
14
62
  presetWind4(),
15
63
  presetTypography({
@@ -47,10 +95,11 @@ export default definePreset(() => {
47
95
  include: [
48
96
  /\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/,
49
97
  "src/**/*.{js,ts,jsx,tsx,vue,svelte,astro}",
98
+ uiSrcGlob,
50
99
  "../ui/src/**/*.{js,ts,jsx,tsx,vue,svelte,astro}"
51
100
  ]
52
101
  },
53
- filesystem: ["../ui/src/**/*", "src/components/**/*", "src/pages/**/*"]
102
+ filesystem: [uiFilesystemGlob, "../ui/src/**/*", "src/components/**/*", "src/pages/**/*"]
54
103
  },
55
104
 
56
105
  shortcuts: {
@@ -94,16 +143,7 @@ export default definePreset(() => {
94
143
  DEFAULT: "var(--popover)",
95
144
  foreground: "var(--popover-foreground)"
96
145
  },
97
- "primary": {
98
- DEFAULT: "var(--primary)",
99
- foreground: "var(--primary-foreground)",
100
- accent: "var(--primary-accent)"
101
- },
102
- "secondary": {
103
- DEFAULT: "var(--secondary)",
104
- foreground: "var(--secondary-foreground)",
105
- accent: "var(--secondary-accent)"
106
- },
146
+ ...Object.fromEntries(allColors.map(color => [color, createScale(color)])),
107
147
  "muted": {
108
148
  DEFAULT: "var(--muted)",
109
149
  foreground: "var(--muted-foreground)"
@@ -112,34 +152,6 @@ export default definePreset(() => {
112
152
  DEFAULT: "var(--accent)",
113
153
  foreground: "var(--accent-foreground)"
114
154
  },
115
- "info": {
116
- DEFAULT: "var(--info)",
117
- foreground: "var(--info-foreground)"
118
- },
119
- "success": {
120
- DEFAULT: "var(--success)",
121
- foreground: "var(--success-foreground)"
122
- },
123
- "warning": {
124
- DEFAULT: "var(--warning)",
125
- foreground: "var(--warning-foreground)"
126
- },
127
- "error": {
128
- DEFAULT: "var(--error)",
129
- foreground: "var(--error-foreground)"
130
- },
131
- "commentary": {
132
- DEFAULT: "var(--commentary)",
133
- foreground: "var(--commentary-foreground)"
134
- },
135
- "ideation": {
136
- DEFAULT: "var(--ideation)",
137
- foreground: "var(--ideation-foreground)"
138
- },
139
- "source": {
140
- DEFAULT: "var(--source)",
141
- foreground: "var(--source-foreground)"
142
- },
143
155
  "border": "var(--border)",
144
156
  "input": "var(--input)",
145
157
  "outline": "var(--outline)",
@@ -209,159 +221,7 @@ export default definePreset(() => {
209
221
  }
210
222
  },
211
223
 
212
- preflights: [
213
- {
214
- getCSS: () => `
215
- :root {
216
- --background: #ffffff;
217
- --foreground: #030712;
218
- --card: #ffffff;
219
- --card-foreground: #030712;
220
- --popover: #ffffff;
221
- --popover-foreground: #030712;
222
- --primary: #f97316;
223
- --primary-foreground: #f9fafb;
224
- --primary-accent: #ea580c;
225
- --secondary: #8b5cf6;
226
- --secondary-foreground: #f9fafb;
227
- --secondary-accent: #7c3aed;
228
- --muted: #f3f4f6;
229
- --muted-foreground: #4b5563;
230
- --accent: #f3f4f6;
231
- --accent-foreground: #111827;
232
- --info: #3b82f6;
233
- --info-foreground: #ffffff;
234
- --success: #22c55e;
235
- --success-foreground: #ffffff;
236
- --warning: #f59e0b;
237
- --warning-foreground: #000000;
238
- --error: #ef4444;
239
- --error-foreground: #f9fafb;
240
- --commentary: #ec4899;
241
- --commentary-foreground: #f9fafb;
242
- --ideation: #8b5cf6;
243
- --ideation-foreground: #f9fafb;
244
- --source: #38bdf8;
245
- --source-foreground: #0f172a;
246
- --border: #e5e7eb;
247
- --input: #e5e7eb;
248
- --outline: #9ca3af;
249
- --ring: #9ca3af;
250
- --radius: 0.625rem;
251
- --sidebar: #f9fafb;
252
- --sidebar-foreground: #030712;
253
- --sidebar-primary: #1d4ed8;
254
- --sidebar-primary-foreground: #f9fafb;
255
- --sidebar-accent: #f3f4f6;
256
- --sidebar-accent-foreground: #111827;
257
- --sidebar-border: #e5e7eb;
258
- --sidebar-ring: #9ca3af;
259
- --chart-1: #3b82f6;
260
- --chart-2: #6366f1;
261
- --chart-3: #8b5cf6;
262
- --chart-4: #a855f7;
263
- --chart-5: #ec4899;
264
- --destructive: #ef4444;
265
- --color-surface: #ffffff;
266
- --color-muted-strong: #e5e7eb;
267
- --color-foreground-secondary: #4b5563;
268
- --color-foreground-tertiary: #6b7280;
269
- --color-placeholder: #9ca3af;
270
- --color-input-border: #d1d5db;
271
- --color-hover: #f3f4f6;
272
- --color-destructive-subtle: #fef2f2;
273
- --color-accent-subtle: #eff6ff;
274
- --font-sans: "Noto Sans", sans-serif;
275
- --font-serif: "Noto Serif", serif;
276
- --font-mono: "Noto Sans Mono", monospace;
277
- }
278
-
279
- .dark {
280
- --background: #030712;
281
- --foreground: #f9fafb;
282
- --card: #111827;
283
- --card-foreground: #f9fafb;
284
- --popover: #1f2937;
285
- --popover-foreground: #f9fafb;
286
- --primary: #f97316;
287
- --primary-foreground: #f9fafb;
288
- --primary-accent: #fb923c;
289
- --secondary: #8b5cf6;
290
- --secondary-foreground: #f9fafb;
291
- --secondary-accent: #a78bfa;
292
- --muted: #1f2937;
293
- --muted-foreground: #9ca3af;
294
- --accent: #374151;
295
- --accent-foreground: #f3f4f6;
296
- --info: #3b82f6;
297
- --info-foreground: #ffffff;
298
- --success: #22c55e;
299
- --success-foreground: #ffffff;
300
- --warning: #f59e0b;
301
- --warning-foreground: #000000;
302
- --error: #ef4444;
303
- --error-foreground: #f9fafb;
304
- --commentary: #f472b6;
305
- --commentary-foreground: #0f172a;
306
- --ideation: #a78bfa;
307
- --ideation-foreground: #0f172a;
308
- --source: #7dd3fc;
309
- --source-foreground: #0f172a;
310
- --border: rgba(249, 250, 251, 0.1);
311
- --input: rgba(249, 250, 251, 0.15);
312
- --outline: #6b7280;
313
- --ring: #6b7280;
314
- --sidebar: #111827;
315
- --sidebar-foreground: #f9fafb;
316
- --sidebar-primary: #1d4ed8;
317
- --sidebar-primary-foreground: #f9fafb;
318
- --sidebar-accent: #1f2937;
319
- --sidebar-accent-foreground: #f3f4f6;
320
- --sidebar-border: #1f2937;
321
- --sidebar-ring: #6b7280;
322
- --chart-1: #60a5fa;
323
- --chart-2: #818cf8;
324
- --chart-3: #a78bfa;
325
- --chart-4: #c084fc;
326
- --chart-5: #f472b6;
327
- --destructive: #f87171;
328
- --color-surface: #1f2937;
329
- --color-muted-strong: #374151;
330
- --color-foreground-secondary: #d1d5db;
331
- --color-foreground-tertiary: #9ca3af;
332
- --color-placeholder: #6b7280;
333
- --color-input-border: #4b5563;
334
- --color-hover: #374151;
335
- --color-destructive-subtle: #451a1a;
336
- --color-accent-subtle: #1e293b;
337
- }
338
-
339
- * {
340
- border-color: var(--border);
341
- outline-color: rgba(156, 163, 175, 0.5);
342
- }
343
-
344
- body {
345
- background-color: var(--background);
346
- color: var(--foreground);
347
- font-family: var(--font-sans);
348
- }
349
-
350
- button {
351
- cursor: pointer;
352
- }
353
-
354
- ::selection {
355
- background-color: var(--primary);
356
- color: var(--primary-foreground);
357
- }
358
-
359
- @view-transition {
360
- navigation: auto;
361
- }
362
- `
363
- }
364
- ],
224
+ preflights: [],
365
225
 
366
226
  async configResolved(config) {
367
227
  if (process.env.NODE_ENV === "development" && !process.env.UNO_CONFIG_LOGGED) {
@@ -1,6 +1,8 @@
1
1
  import { defineTheme } from "../utils/defineTheme"
2
2
 
3
- export default defineTheme({
3
+ const defaultColors = ["primary", "secondary", "info", "success", "warning", "error", "commentary", "ideation", "source"];
4
+
5
+ export const createButtonTheme = (colors: string[] = defaultColors) => defineTheme({
4
6
  slots: ["root", "label", "leadingIcon", "trailingIcon"],
5
7
  base: {
6
8
  root: [
@@ -16,23 +18,15 @@ export default defineTheme({
16
18
  },
17
19
  variants: {
18
20
  variant: {
19
- solid: { root: "b shadow-xs" },
20
- outline: { root: "b shadow-xs border" },
21
+ solid: { root: "shadow-sm border border-transparent" },
22
+ outline: { root: "b shadow-sm border" },
21
23
  soft: { root: "" },
22
24
  subtle: { root: "" },
23
25
  ghost: { root: "" },
24
- link: { root: "p-0 h-auto" }
26
+ link: { root: "p-0 h-auto no-underline" }
25
27
  },
26
28
  color: {
27
- primary: { root: "" },
28
- secondary: { root: "" },
29
- info: { root: "" },
30
- success: { root: "" },
31
- warning: { root: "" },
32
- error: { root: "" },
33
- commentary: { root: "" },
34
- ideation: { root: "" },
35
- source: { root: "" }
29
+ ...Object.fromEntries(colors.map(color => [color, { root: "" }]))
36
30
  },
37
31
  size: {
38
32
  xs: {
@@ -76,384 +70,48 @@ export default defineTheme({
76
70
  }
77
71
  },
78
72
  compoundVariants: [
79
- {
80
- variant: "solid",
81
- color: "primary",
82
- classNames: {
83
- root: "bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary/50"
84
- }
85
- },
86
- {
87
- variant: "solid",
88
- color: "secondary",
89
- classNames: {
90
- root: "bg-secondary text-secondary-foreground hover:bg-secondary/90 focus-visible:ring-secondary/50"
91
- }
92
- },
93
- {
94
- variant: "solid",
95
- color: "info",
96
- classNames: {
97
- root: "bg-info text-info-foreground hover:bg-info/90 focus-visible:ring-info/50"
98
- }
99
- },
100
- {
101
- variant: "solid",
102
- color: "success",
103
- classNames: {
104
- root: "bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success/50"
105
- }
106
- },
107
- {
108
- variant: "solid",
109
- color: "warning",
110
- classNames: {
111
- root: "bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning/50"
112
- }
113
- },
114
- {
115
- variant: "solid",
116
- color: "error",
117
- classNames: {
118
- root: "bg-error text-error-foreground hover:bg-error/90 focus-visible:ring-error/50"
119
- }
120
- },
121
- {
122
- variant: "solid",
123
- color: "commentary",
124
- classNames: {
125
- root: "bg-commentary text-commentary-foreground hover:bg-commentary/90 focus-visible:ring-commentary/50"
126
- }
127
- },
128
- {
129
- variant: "solid",
130
- color: "ideation",
131
- classNames: {
132
- root: "bg-ideation text-ideation-foreground hover:bg-ideation/90 focus-visible:ring-ideation/50"
133
- }
134
- },
135
- {
136
- variant: "solid",
137
- color: "source",
138
- classNames: {
139
- root: "bg-source text-source-foreground hover:bg-source/90 focus-visible:ring-source/50"
140
- }
141
- },
142
- {
143
- variant: "outline",
144
- color: "primary",
145
- classNames: {
146
- root: "border-primary/20 text-primary-600 bg-primary/5 hover:bg-primary/10 hover:border-primary/30 focus-visible:ring-primary/40"
147
- }
148
- },
149
- {
150
- variant: "outline",
151
- color: "secondary",
152
- classNames: {
153
- root: "border-secondary/20 text-secondary-600 bg-secondary/5 hover:bg-secondary/10 hover:border-secondary/30 focus-visible:ring-secondary/40"
154
- }
155
- },
156
- {
157
- variant: "outline",
158
- color: "info",
159
- classNames: {
160
- root: "border-info/20 text-info-600 bg-info/5 hover:bg-info/10 hover:border-info/30 focus-visible:ring-info/40"
161
- }
162
- },
163
- {
164
- variant: "outline",
165
- color: "success",
166
- classNames: {
167
- root: "border-success/20 text-success-600 bg-success/5 hover:bg-success/10 hover:border-success/30 focus-visible:ring-success/40"
168
- }
169
- },
170
- {
171
- variant: "outline",
172
- color: "warning",
173
- classNames: {
174
- root: "border-warning/20 text-warning-600 bg-warning/5 hover:bg-warning/10 hover:border-warning/30 focus-visible:ring-warning/40"
175
- }
176
- },
177
- {
178
- variant: "outline",
179
- color: "error",
180
- classNames: {
181
- root: "border-error/20 text-error-600 bg-error/5 hover:bg-error/10 hover:border-error/30 focus-visible:ring-error/40"
182
- }
183
- },
184
- {
185
- variant: "outline",
186
- color: "commentary",
187
- classNames: {
188
- root: "border-commentary/20 text-commentary-600 bg-commentary/5 hover:bg-commentary/10 hover:border-commentary/30 focus-visible:ring-commentary/40"
189
- }
190
- },
191
- {
192
- variant: "outline",
193
- color: "ideation",
194
- classNames: {
195
- root: "border-ideation/20 text-ideation-600 bg-ideation/5 hover:bg-ideation/10 hover:border-ideation/30 focus-visible:ring-ideation/40"
196
- }
197
- },
198
- {
199
- variant: "outline",
200
- color: "source",
201
- classNames: {
202
- root: "border-source/20 text-source-600 bg-source/5 hover:bg-source/10 hover:border-source/30 focus-visible:ring-source/40"
203
- }
204
- },
205
- {
206
- variant: "subtle",
207
- color: "primary",
208
- classNames: {
209
- root: "text-primary-600 bg-primary/10 hover:bg-primary/20 focus-visible:ring-primary/40"
210
- }
211
- },
212
- {
213
- variant: "subtle",
214
- color: "secondary",
215
- classNames: {
216
- root: "text-secondary-600 bg-secondary/10 hover:bg-secondary/20 focus-visible:ring-secondary/40"
217
- }
218
- },
219
- {
220
- variant: "subtle",
221
- color: "info",
222
- classNames: {
223
- root: "text-info-600 bg-info/10 hover:bg-info/20 focus-visible:ring-info/40"
224
- }
225
- },
226
- {
227
- variant: "subtle",
228
- color: "success",
229
- classNames: {
230
- root: "text-success-600 bg-success/10 hover:bg-success/20 focus-visible:ring-success/40"
231
- }
232
- },
233
- {
234
- variant: "subtle",
235
- color: "warning",
236
- classNames: {
237
- root: "text-warning-600 bg-warning/10 hover:bg-warning/20 focus-visible:ring-warning/40"
238
- }
239
- },
240
- {
241
- variant: "subtle",
242
- color: "error",
243
- classNames: {
244
- root: "text-error-600 bg-error/10 hover:bg-error/20 focus-visible:ring-error/40"
245
- }
246
- },
247
- {
248
- variant: "subtle",
249
- color: "commentary",
250
- classNames: {
251
- root: "text-commentary-600 bg-commentary/10 hover:bg-commentary/20 focus-visible:ring-commentary/40"
252
- }
253
- },
254
- {
255
- variant: "subtle",
256
- color: "ideation",
257
- classNames: {
258
- root: "text-ideation-600 bg-ideation/10 hover:bg-ideation/20 focus-visible:ring-ideation/40"
259
- }
260
- },
261
- {
262
- variant: "subtle",
263
- color: "source",
264
- classNames: {
265
- root: "text-source-600 bg-source/10 hover:bg-source/20 focus-visible:ring-source/40"
266
- }
267
- },
268
- {
269
- variant: "ghost",
270
- color: "primary",
271
- classNames: {
272
- root: "text-primary-600 hover:bg-primary/10 focus-visible:ring-primary/40"
273
- }
274
- },
275
- {
276
- variant: "ghost",
277
- color: "secondary",
278
- classNames: {
279
- root: "text-secondary-600 hover:bg-secondary/10 focus-visible:ring-secondary/40"
280
- }
281
- },
282
- {
283
- variant: "ghost",
284
- color: "info",
285
- classNames: {
286
- root: "text-info-600 hover:bg-info/10 focus-visible:ring-info/40"
287
- }
288
- },
289
- {
290
- variant: "ghost",
291
- color: "success",
292
- classNames: {
293
- root: "text-success-600 hover:bg-success/10 focus-visible:ring-success/40"
294
- }
295
- },
296
- {
297
- variant: "ghost",
298
- color: "warning",
299
- classNames: {
300
- root: "text-warning-600 hover:bg-warning/10 focus-visible:ring-warning/40"
301
- }
302
- },
303
- {
304
- variant: "ghost",
305
- color: "error",
306
- classNames: {
307
- root: "text-error-600 hover:bg-error/10 focus-visible:ring-error/40"
308
- }
309
- },
310
- {
311
- variant: "ghost",
312
- color: "commentary",
313
- classNames: {
314
- root: "text-commentary-600 hover:bg-commentary/10 focus-visible:ring-commentary/40"
315
- }
316
- },
317
- {
318
- variant: "ghost",
319
- color: "ideation",
320
- classNames: {
321
- root: "text-ideation-600 hover:bg-ideation/10 hover:bg-ideation/20 focus-visible:ring-ideation/40"
322
- }
323
- },
324
- {
325
- variant: "ghost",
326
- color: "source",
73
+ ...colors.map(color => ({
74
+ variant: "solid" as const,
75
+ color,
327
76
  classNames: {
328
- root: "text-source-600 hover:bg-source/10 focus-visible:ring-source/40"
77
+ root: `bg-${color}-500 text-white hover:bg-${color}-600 focus-visible:ring-${color}-500/50`
329
78
  }
330
- },
331
- {
332
- variant: "link",
333
- color: "primary",
79
+ })),
80
+ ...colors.map(color => ({
81
+ variant: "outline" as const,
82
+ color,
334
83
  classNames: {
335
- root: "text-primary-600 hover:underline hover:decoration-primary/30"
84
+ root: `border-${color}-500/20 text-${color}-600 bg-${color}-500/5 hover:bg-${color}-500/10 hover:border-${color}-500/30 focus-visible:ring-${color}-500/40`
336
85
  }
337
- },
338
- {
339
- variant: "link",
340
- color: "secondary",
86
+ })),
87
+ ...colors.map(color => ({
88
+ variant: "soft" as const,
89
+ color,
341
90
  classNames: {
342
- root: "text-secondary-600 hover:underline hover:decoration-secondary/30"
91
+ root: `text-${color}-600 bg-${color}-500/10 hover:bg-${color}-500/20 focus-visible:ring-${color}-500/40`
343
92
  }
344
- },
345
- {
346
- variant: "link",
347
- color: "info",
93
+ })),
94
+ ...colors.map(color => ({
95
+ variant: "subtle" as const,
96
+ color,
348
97
  classNames: {
349
- root: "text-info-600 hover:underline hover:decoration-info/30"
98
+ root: `text-${color}-600 bg-${color}-500/10 hover:bg-${color}-500/20 focus-visible:ring-${color}-500/40`
350
99
  }
351
- },
352
- {
353
- variant: "link",
354
- color: "success",
100
+ })),
101
+ ...colors.map(color => ({
102
+ variant: "ghost" as const,
103
+ color,
355
104
  classNames: {
356
- root: "text-success-600 hover:underline hover:decoration-success/30"
105
+ root: `text-${color}-600 hover:bg-${color}-500/10 focus-visible:ring-${color}-500/40`
357
106
  }
358
- },
359
- {
360
- variant: "link",
361
- color: "warning",
107
+ })),
108
+ ...colors.map(color => ({
109
+ variant: "link" as const,
110
+ color,
362
111
  classNames: {
363
- root: "text-warning-600 hover:underline hover:decoration-warning/30"
112
+ root: `text-${color}-600 hover:underline hover:decoration-${color}-300`
364
113
  }
365
- },
366
- {
367
- variant: "link",
368
- color: "error",
369
- classNames: {
370
- root: "text-error-600 hover:underline hover:decoration-error/30"
371
- }
372
- },
373
- {
374
- variant: "link",
375
- color: "commentary",
376
- classNames: {
377
- root: "text-commentary-600 hover:underline hover:decoration-commentary/30"
378
- }
379
- },
380
- {
381
- variant: "link",
382
- color: "ideation",
383
- classNames: {
384
- root: "text-ideation-600 hover:underline hover:decoration-ideation/30"
385
- }
386
- },
387
- {
388
- variant: "link",
389
- color: "source",
390
- classNames: {
391
- root: "text-source-600 hover:underline hover:decoration-source/30"
392
- }
393
- },
394
- {
395
- variant: "soft",
396
- color: "primary",
397
- classNames: {
398
- root: "text-primary-600 bg-primary/10 hover:bg-primary/20 focus-visible:ring-primary/40"
399
- }
400
- },
401
- {
402
- variant: "soft",
403
- color: "secondary",
404
- classNames: {
405
- root: "text-secondary-600 bg-secondary/10 hover:bg-secondary/20 focus-visible:ring-secondary/40"
406
- }
407
- },
408
- {
409
- variant: "soft",
410
- color: "info",
411
- classNames: {
412
- root: "text-info-600 bg-info/10 hover:bg-info/20 focus-visible:ring-info/40"
413
- }
414
- },
415
- {
416
- variant: "soft",
417
- color: "success",
418
- classNames: {
419
- root: "text-success-600 bg-success/10 hover:bg-success/20 focus-visible:ring-success/40"
420
- }
421
- },
422
- {
423
- variant: "soft",
424
- color: "warning",
425
- classNames: {
426
- root: "text-warning-600 bg-warning/10 hover:bg-warning/20 focus-visible:ring-warning/40"
427
- }
428
- },
429
- {
430
- variant: "soft",
431
- color: "error",
432
- classNames: {
433
- root: "text-error-600 bg-error/10 hover:bg-error/20 focus-visible:ring-error/40"
434
- }
435
- },
436
- {
437
- variant: "soft",
438
- color: "commentary",
439
- classNames: {
440
- root: "text-commentary-600 bg-commentary/10 hover:bg-commentary/20 focus-visible:ring-commentary/40"
441
- }
442
- },
443
- {
444
- variant: "soft",
445
- color: "ideation",
446
- classNames: {
447
- root: "text-ideation-600 bg-ideation/10 hover:bg-ideation/20 focus-visible:ring-ideation/40"
448
- }
449
- },
450
- {
451
- variant: "soft",
452
- color: "source",
453
- classNames: {
454
- root: "text-source-600 bg-source/10 hover:bg-source/20 focus-visible:ring-source/40"
455
- }
456
- },
114
+ })),
457
115
  {
458
116
  square: true,
459
117
  size: "xs",
@@ -519,3 +177,5 @@ export default defineTheme({
519
177
  size: "md"
520
178
  }
521
179
  })
180
+
181
+ export default createButtonTheme()
@@ -0,0 +1,19 @@
1
+ import { defineTheme } from "../utils/defineTheme"
2
+
3
+ export default defineTheme({
4
+ slots: [
5
+ "root",
6
+ "trigger",
7
+ "image",
8
+ "icon"
9
+ ],
10
+ base: {
11
+ root: "relative aspect-video h-full w-full cursor-pointer overflow-hidden",
12
+ trigger: "group absolute inset-0 aspect-video h-full w-full overflow-hidden",
13
+ image: "h-full w-full object-cover",
14
+ icon: "absolute inset-0 m-auto h-24 w-24 text-white opacity-75 transition-opacity group-hover:opacity-100"
15
+ },
16
+ variants: {},
17
+ compoundVariants: [],
18
+ defaultVariants: {}
19
+ })