@refraction-ui/astro 0.10.0 → 0.11.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.
|
@@ -37,40 +37,45 @@ const {
|
|
|
37
37
|
aria-label="Cookie consent"
|
|
38
38
|
class={cn('fixed inset-x-0 z-50 p-4', position === 'bottom' ? 'bottom-0' : 'top-0', className)}
|
|
39
39
|
>
|
|
40
|
-
<div class="mx-auto max-w-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
<div class="mx-auto max-w-2xl overflow-hidden rounded-2xl border border-border bg-background shadow-lg">
|
|
41
|
+
<div data-rfr-cc-prompt class="flex flex-col gap-4 p-5 sm:flex-row sm:items-center">
|
|
42
|
+
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent text-xl" aria-hidden="true">🍪</div>
|
|
43
|
+
<div class="min-w-0 flex-1">
|
|
44
|
+
<p class="text-sm font-semibold">{title}</p>
|
|
45
|
+
<p class="mt-0.5 text-sm leading-relaxed text-muted-foreground">
|
|
46
|
+
{description}
|
|
47
|
+
{policyUrl && (
|
|
48
|
+
<a href={policyUrl} target="_blank" rel="noreferrer" class="font-medium text-muted-foreground underline-offset-4 hover:text-foreground hover:underline"> {policyLabel}</a>
|
|
49
|
+
)}
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="flex flex-wrap items-center gap-2 sm:shrink-0">
|
|
53
|
+
<button type="button" data-rfr-cc-manage class="text-sm font-medium text-muted-foreground underline-offset-4 hover:text-foreground hover:underline">Customize</button>
|
|
54
|
+
<button type="button" data-rfr-cc-reject class="inline-flex items-center rounded-lg border border-border px-3.5 py-2 text-sm font-medium hover:bg-accent">Reject all</button>
|
|
55
|
+
<button type="button" data-rfr-cc-accept class="inline-flex items-center rounded-lg bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground hover:opacity-90">Accept all</button>
|
|
56
|
+
</div>
|
|
51
57
|
</div>
|
|
52
58
|
|
|
53
|
-
<div data-rfr-cc-settings hidden class="
|
|
59
|
+
<div data-rfr-cc-settings hidden class="space-y-2 p-5">
|
|
60
|
+
<p class="text-sm font-semibold">Cookie preferences</p>
|
|
54
61
|
{categories.map((cat) => (
|
|
55
|
-
<label class="flex items-
|
|
62
|
+
<label class="flex items-center justify-between gap-4 rounded-xl border border-border p-3">
|
|
56
63
|
<span class="min-w-0">
|
|
57
|
-
<span class="block text-sm font-medium">{cat.label}
|
|
58
|
-
{cat.description && <span class="block text-xs text-muted-foreground">{cat.description}</span>}
|
|
64
|
+
<span class="block text-sm font-medium">{cat.label}</span>
|
|
65
|
+
{cat.description && <span class="mt-0.5 block text-xs text-muted-foreground">{cat.description}</span>}
|
|
59
66
|
</span>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class="mt-0.5 h-4 w-4"
|
|
67
|
-
/>
|
|
67
|
+
{cat.required ? (
|
|
68
|
+
<span class="rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground">Always on</span>
|
|
69
|
+
) : (
|
|
70
|
+
<input type="checkbox" data-rfr-cc-cat={cat.id} aria-label={cat.label} class="h-5 w-5 accent-[hsl(var(--primary))]" />
|
|
71
|
+
)}
|
|
72
|
+
{cat.required && <input type="checkbox" data-rfr-cc-cat={cat.id} checked disabled hidden />}
|
|
68
73
|
</label>
|
|
69
74
|
))}
|
|
70
75
|
<div class="flex flex-wrap items-center gap-2 pt-1">
|
|
71
|
-
<button type="button" data-rfr-cc-
|
|
72
|
-
<button type="button" data-rfr-cc-accept class="rounded-
|
|
73
|
-
<button type="button" data-rfr-cc-
|
|
76
|
+
<button type="button" data-rfr-cc-back class="text-sm font-medium text-muted-foreground underline-offset-4 hover:text-foreground hover:underline">← Back</button>
|
|
77
|
+
<button type="button" data-rfr-cc-accept class="inline-flex items-center rounded-lg border border-border px-3.5 py-2 text-sm font-medium hover:bg-accent sm:ml-auto">Accept all</button>
|
|
78
|
+
<button type="button" data-rfr-cc-save class="inline-flex items-center rounded-lg bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground hover:opacity-90">Save preferences</button>
|
|
74
79
|
</div>
|
|
75
80
|
</div>
|
|
76
81
|
</div>
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { getThemeScript } from '../theme/index.ts'
|
|
2
|
+
import { getThemeScript, type ThemeMode } from '../theme/index.ts'
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
5
|
storageKey?: string
|
|
6
6
|
attribute?: 'class' | 'data-theme'
|
|
7
|
+
/**
|
|
8
|
+
* Theme to apply when nothing is stored. Match this to the `<ThemeProvider>`
|
|
9
|
+
* client component's `defaultMode` so first-paint and hydration agree. Issue #317.
|
|
10
|
+
*/
|
|
11
|
+
defaultMode?: ThemeMode
|
|
12
|
+
/** When `false`, never fall through to `prefers-color-scheme`. Default `true`. */
|
|
13
|
+
enableSystem?: boolean
|
|
7
14
|
}
|
|
8
15
|
|
|
9
|
-
const {
|
|
10
|
-
|
|
16
|
+
const {
|
|
17
|
+
storageKey = 'rfr-theme',
|
|
18
|
+
attribute = 'class',
|
|
19
|
+
defaultMode = 'system',
|
|
20
|
+
enableSystem = true,
|
|
21
|
+
} = Astro.props
|
|
22
|
+
|
|
23
|
+
const scriptContent = getThemeScript({ storageKey, attribute, defaultMode, enableSystem })
|
|
11
24
|
---
|
|
12
25
|
|
|
13
26
|
<script set:html={scriptContent} />
|
|
@@ -17,6 +17,10 @@ export const buttonVariants = cva({
|
|
|
17
17
|
variants: {
|
|
18
18
|
variant: {
|
|
19
19
|
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
20
|
+
// Alias for `default` — see resolveButtonVariant + issue #201. Same classes
|
|
21
|
+
// so styling stays identical even if a consumer reaches into `buttonVariants`
|
|
22
|
+
// directly without going through resolveButtonVariant.
|
|
23
|
+
primary: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
20
24
|
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
|
21
25
|
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
|
22
26
|
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
package/dist/button/button.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import type { AccessibilityProps, KeyboardHandlerMap } from '../shared/index.ts'
|
|
2
2
|
import { Keys } from '../shared/index.ts'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* `primary` is an alias of `default` — included so the universal React-ecosystem
|
|
6
|
+
* muscle-memory (`<Button variant="primary">`) renders as the most-emphasized
|
|
7
|
+
* button instead of falling through to the unstyled base. See issue #201.
|
|
8
|
+
*/
|
|
9
|
+
export type ButtonVariant =
|
|
10
|
+
| 'default'
|
|
11
|
+
| 'primary'
|
|
12
|
+
| 'destructive'
|
|
13
|
+
| 'outline'
|
|
14
|
+
| 'secondary'
|
|
15
|
+
| 'ghost'
|
|
16
|
+
| 'link'
|
|
17
|
+
|
|
18
|
+
/** Variants that map to the same visual style as `default`. */
|
|
19
|
+
export const BUTTON_VARIANT_ALIASES: Partial<Record<ButtonVariant, ButtonVariant>> = {
|
|
20
|
+
primary: 'default',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Resolve an alias to its canonical variant. Pass-through for unknown/undefined. */
|
|
24
|
+
export function resolveButtonVariant(variant: ButtonVariant | undefined): ButtonVariant | undefined {
|
|
25
|
+
if (!variant) return variant
|
|
26
|
+
return BUTTON_VARIANT_ALIASES[variant] ?? variant
|
|
27
|
+
}
|
|
5
28
|
export type ButtonSize = 'xs' | 'sm' | 'default' | 'lg' | 'icon'
|
|
6
29
|
|
|
7
30
|
export interface ButtonProps {
|
package/dist/button/index.ts
CHANGED
|
@@ -4,15 +4,79 @@
|
|
|
4
4
|
* Works with any framework (React, Astro, plain HTML).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { ThemeMode } from './theme-machine.js'
|
|
8
|
+
|
|
9
|
+
export interface ThemeScriptOptions {
|
|
10
|
+
/** localStorage key the corresponding ThemeProvider reads. */
|
|
11
|
+
storageKey?: string
|
|
12
|
+
/** Attribute strategy: 'class' toggles `light`/`dark` classes; otherwise an HTML attribute. */
|
|
13
|
+
attribute?: 'class' | 'data-theme'
|
|
14
|
+
/**
|
|
15
|
+
* Theme to apply when nothing is stored AND `enableSystem` is false
|
|
16
|
+
* (or `defaultMode` is explicitly 'light'/'dark'). Match this to your
|
|
17
|
+
* ThemeProvider's `defaultMode` so the pre-paint script and provider agree.
|
|
18
|
+
* Defaults to `'system'` — current behavior, OS preference wins.
|
|
19
|
+
*
|
|
20
|
+
* Issue #317.
|
|
21
|
+
*/
|
|
22
|
+
defaultMode?: ThemeMode
|
|
23
|
+
/**
|
|
24
|
+
* When `true` (default) and `defaultMode === 'system'`, the script falls
|
|
25
|
+
* through to `prefers-color-scheme`. When `false`, the script applies
|
|
26
|
+
* `defaultMode` directly and never reads the OS preference.
|
|
27
|
+
*/
|
|
28
|
+
enableSystem?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build the pre-paint inline script. Mirrors the resolution order used by
|
|
33
|
+
* the React/Astro `ThemeProvider`:
|
|
34
|
+
*
|
|
35
|
+
* 1. stored value (light|dark|system)
|
|
36
|
+
* 2. if no stored value: `defaultMode` (light|dark applied as-is)
|
|
37
|
+
* 3. if stored or default is 'system' AND `enableSystem`: matchMedia
|
|
38
|
+
* 4. else: 'light'
|
|
39
|
+
*
|
|
40
|
+
* Back-compat: with no options the script behaves exactly as before —
|
|
41
|
+
* stored value, then matchMedia, then 'light'.
|
|
42
|
+
*/
|
|
7
43
|
export function getThemeScript(
|
|
8
|
-
|
|
44
|
+
storageKeyOrOptions: string | ThemeScriptOptions = 'rfr-theme',
|
|
9
45
|
attribute: 'class' | 'data-theme' = 'class',
|
|
10
46
|
): string {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
47
|
+
const opts: Required<ThemeScriptOptions> =
|
|
48
|
+
typeof storageKeyOrOptions === 'string'
|
|
49
|
+
? {
|
|
50
|
+
storageKey: storageKeyOrOptions,
|
|
51
|
+
attribute,
|
|
52
|
+
defaultMode: 'system',
|
|
53
|
+
enableSystem: true,
|
|
54
|
+
}
|
|
55
|
+
: {
|
|
56
|
+
storageKey: storageKeyOrOptions.storageKey ?? 'rfr-theme',
|
|
57
|
+
attribute: storageKeyOrOptions.attribute ?? 'class',
|
|
58
|
+
defaultMode: storageKeyOrOptions.defaultMode ?? 'system',
|
|
59
|
+
enableSystem: storageKeyOrOptions.enableSystem ?? true,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// `noSystemFallback` short-circuits matchMedia entirely when the consumer
|
|
63
|
+
// wants brand consistency over OS preference (defaultMode='light'/'dark'
|
|
64
|
+
// OR enableSystem=false).
|
|
65
|
+
const noSystemFallback =
|
|
66
|
+
opts.defaultMode === 'light' || opts.defaultMode === 'dark' || !opts.enableSystem
|
|
67
|
+
const safeDefault: 'light' | 'dark' =
|
|
68
|
+
opts.defaultMode === 'dark' ? 'dark' : 'light'
|
|
69
|
+
|
|
70
|
+
const resolveExpr = noSystemFallback
|
|
71
|
+
? // Stored wins → else hard default. No matchMedia call at all.
|
|
72
|
+
`var t=m==='dark'?'dark':(m==='light'?'light':'${safeDefault}');`
|
|
73
|
+
: // Original behaviour: stored → else matchMedia → else light.
|
|
74
|
+
`var s=window.matchMedia('(prefers-color-scheme:dark)').matches;var t=m==='dark'||(m!=='light'&&s)?'dark':'light';`
|
|
75
|
+
|
|
76
|
+
const applyExpr =
|
|
77
|
+
opts.attribute === 'class'
|
|
15
78
|
? "d.classList.remove('light','dark');d.classList.add(t);"
|
|
16
|
-
: `d.setAttribute('${attribute}',t);`
|
|
17
|
-
|
|
79
|
+
: `d.setAttribute('${opts.attribute}',t);`
|
|
80
|
+
|
|
81
|
+
return `(function(){try{var m=localStorage.getItem('${opts.storageKey}');${resolveExpr}var d=document.documentElement;${applyExpr}d.style.colorScheme=t;}catch(e){}})()`
|
|
18
82
|
}
|