@actuate-media/cms-admin 0.63.1 → 0.64.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/design-system/README.md +53 -0
  3. package/design-system/tokens.css +114 -0
  4. package/design-system/tokens.json +149 -0
  5. package/design-system/tokens.scss +108 -0
  6. package/design-system/tokens.tailwind.cjs +80 -0
  7. package/dist/__tests__/layout/primitives.test.js +9 -2
  8. package/dist/__tests__/layout/primitives.test.js.map +1 -1
  9. package/dist/actuate-admin.css +1 -1
  10. package/dist/components/EnabledSwitch.d.ts +10 -0
  11. package/dist/components/EnabledSwitch.d.ts.map +1 -0
  12. package/dist/components/EnabledSwitch.js +8 -0
  13. package/dist/components/EnabledSwitch.js.map +1 -0
  14. package/dist/components/ThemeProvider.d.ts.map +1 -1
  15. package/dist/components/ThemeProvider.js +5 -3
  16. package/dist/components/ThemeProvider.js.map +1 -1
  17. package/dist/components/forms/FieldsPanel.js +2 -2
  18. package/dist/components/forms/FieldsPanel.js.map +1 -1
  19. package/dist/components/forms/NotificationsPanel.js +2 -2
  20. package/dist/components/forms/NotificationsPanel.js.map +1 -1
  21. package/dist/components/forms/WebhooksPanel.d.ts.map +1 -1
  22. package/dist/components/forms/WebhooksPanel.js +2 -2
  23. package/dist/components/forms/WebhooksPanel.js.map +1 -1
  24. package/dist/components/seo/primitives.d.ts.map +1 -1
  25. package/dist/components/seo/primitives.js +2 -1
  26. package/dist/components/seo/primitives.js.map +1 -1
  27. package/dist/components/ui/Button.d.ts +1 -1
  28. package/dist/components/ui/Button.js +1 -1
  29. package/dist/components/ui/Button.js.map +1 -1
  30. package/dist/components/ui/Card.d.ts +6 -5
  31. package/dist/components/ui/Card.d.ts.map +1 -1
  32. package/dist/components/ui/Card.js +20 -18
  33. package/dist/components/ui/Card.js.map +1 -1
  34. package/dist/components/ui/Input.d.ts +2 -1
  35. package/dist/components/ui/Input.d.ts.map +1 -1
  36. package/dist/components/ui/Input.js +9 -8
  37. package/dist/components/ui/Input.js.map +1 -1
  38. package/dist/components/ui/SearchInput.d.ts.map +1 -1
  39. package/dist/components/ui/SearchInput.js +2 -1
  40. package/dist/components/ui/SearchInput.js.map +1 -1
  41. package/dist/components/ui/Select.d.ts.map +1 -1
  42. package/dist/components/ui/Select.js +9 -10
  43. package/dist/components/ui/Select.js.map +1 -1
  44. package/dist/components/ui/Toggle.d.ts +31 -0
  45. package/dist/components/ui/Toggle.d.ts.map +1 -0
  46. package/dist/components/ui/Toggle.js +41 -0
  47. package/dist/components/ui/Toggle.js.map +1 -0
  48. package/dist/components/ui/form-control.d.ts +11 -0
  49. package/dist/components/ui/form-control.d.ts.map +1 -0
  50. package/dist/components/ui/form-control.js +11 -0
  51. package/dist/components/ui/form-control.js.map +1 -0
  52. package/dist/components/ui/index.d.ts +3 -0
  53. package/dist/components/ui/index.d.ts.map +1 -1
  54. package/dist/components/ui/index.js +2 -0
  55. package/dist/components/ui/index.js.map +1 -1
  56. package/dist/layout/Header.d.ts.map +1 -1
  57. package/dist/layout/Header.js +5 -2
  58. package/dist/layout/Header.js.map +1 -1
  59. package/dist/layout/Sidebar.js +1 -1
  60. package/dist/layout/Sidebar.js.map +1 -1
  61. package/dist/layout/primitives/tokens.d.ts +10 -4
  62. package/dist/layout/primitives/tokens.d.ts.map +1 -1
  63. package/dist/layout/primitives/tokens.js +10 -4
  64. package/dist/layout/primitives/tokens.js.map +1 -1
  65. package/dist/views/Forms.js +2 -2
  66. package/dist/views/Forms.js.map +1 -1
  67. package/dist/views/ScriptTagEditor.d.ts.map +1 -1
  68. package/dist/views/ScriptTagEditor.js +2 -1
  69. package/dist/views/ScriptTagEditor.js.map +1 -1
  70. package/dist/views/ScriptTags.d.ts.map +1 -1
  71. package/dist/views/ScriptTags.js +29 -8
  72. package/dist/views/ScriptTags.js.map +1 -1
  73. package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
  74. package/dist/views/page-builder/BottomBar.js +2 -2
  75. package/dist/views/page-builder/BottomBar.js.map +1 -1
  76. package/dist/views/seo/TechnicalTab.d.ts.map +1 -1
  77. package/dist/views/seo/TechnicalTab.js +2 -2
  78. package/dist/views/seo/TechnicalTab.js.map +1 -1
  79. package/dist/views/settings/components.d.ts.map +1 -1
  80. package/dist/views/settings/components.js +2 -1
  81. package/dist/views/settings/components.js.map +1 -1
  82. package/package.json +8 -3
  83. package/src/__tests__/layout/primitives.test.ts +10 -2
  84. package/src/components/EnabledSwitch.tsx +16 -0
  85. package/src/components/ThemeProvider.tsx +5 -3
  86. package/src/components/forms/FieldsPanel.tsx +4 -6
  87. package/src/components/forms/NotificationsPanel.tsx +2 -9
  88. package/src/components/forms/WebhooksPanel.tsx +4 -6
  89. package/src/components/seo/primitives.tsx +8 -7
  90. package/src/components/ui/Button.tsx +1 -1
  91. package/src/components/ui/Card.tsx +35 -18
  92. package/src/components/ui/Input.tsx +15 -17
  93. package/src/components/ui/SearchInput.tsx +9 -4
  94. package/src/components/ui/Select.tsx +15 -18
  95. package/src/components/ui/Toggle.tsx +85 -0
  96. package/src/components/ui/form-control.ts +12 -0
  97. package/src/components/ui/index.ts +3 -0
  98. package/src/layout/Header.tsx +6 -2
  99. package/src/layout/Sidebar.tsx +3 -3
  100. package/src/layout/primitives/tokens.ts +11 -4
  101. package/src/styles/actuate-tokens.css +97 -0
  102. package/src/styles/theme.css +88 -156
  103. package/src/views/Forms.tsx +4 -6
  104. package/src/views/ScriptTagEditor.tsx +6 -12
  105. package/src/views/ScriptTags.tsx +40 -19
  106. package/src/views/page-builder/BottomBar.tsx +3 -6
  107. package/src/views/seo/TechnicalTab.tsx +3 -6
  108. package/src/views/settings/components.tsx +11 -10
@@ -2,29 +2,31 @@ import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'
2
2
  import { cv, type VariantProps } from '../../lib/cv.js'
3
3
 
4
4
  const card = cv(
5
- 'rounded-[var(--radius)] border border-[var(--border)] bg-[var(--card)] text-[var(--card-foreground)]',
5
+ 'overflow-hidden rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] text-[var(--txt)]',
6
6
  {
7
7
  variants: {
8
8
  padding: {
9
9
  none: 'p-0',
10
- sm: 'p-4',
11
- md: 'p-6',
12
- lg: 'p-8',
10
+ /** Stat / compact surfaces (`.dstat`). */
11
+ sm: 'px-4 py-3.5',
12
+ /** Simple padded panel (`.dcard-b`). */
13
+ md: 'px-[18px] py-4',
14
+ lg: 'px-[18px] py-6',
13
15
  },
14
16
  elevation: {
15
17
  flat: 'shadow-none',
16
- sm: 'shadow-sm',
17
- md: 'shadow-md',
18
- lg: 'shadow-lg',
18
+ resting: 'shadow-[var(--shd)]',
19
+ elevated: 'shadow-[var(--shd2)]',
19
20
  },
20
21
  },
21
- defaultVariants: { padding: 'md', elevation: 'sm' },
22
+ defaultVariants: { padding: 'none', elevation: 'resting' },
22
23
  },
23
24
  )
24
25
 
25
26
  export type CardVariants = VariantProps<typeof card>
26
27
 
27
- const INTERACTIVE_CLASS = 'transition-shadow hover:shadow-md focus-within:shadow-md'
28
+ const INTERACTIVE_CLASS =
29
+ 'motion-safe:transition-shadow motion-safe:duration-[var(--motion-fast)] hover:shadow-[var(--shd2)] focus-within:shadow-[var(--shd2)]'
28
30
 
29
31
  export interface CardProps extends HTMLAttributes<HTMLDivElement>, CardVariants {
30
32
  /** Apply hover/focus elevation transitions (e.g. for clickable cards). */
@@ -44,12 +46,20 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(function Card(
44
46
  )
45
47
  })
46
48
 
47
- /**
48
- * Header slot — sits at the top of a card. Pair with CardTitle / CardDescription.
49
- */
50
- export function CardHeader({ className = '', children, ...rest }: HTMLAttributes<HTMLDivElement>) {
49
+ /** Header slot — `.dcard-h` in the design system handoff. */
50
+ export function CardHeader({
51
+ className = '',
52
+ bordered = true,
53
+ children,
54
+ ...rest
55
+ }: HTMLAttributes<HTMLDivElement> & { bordered?: boolean }) {
51
56
  return (
52
- <div className={`flex flex-col space-y-1.5 ${className}`} {...rest}>
57
+ <div
58
+ className={[bordered ? 'border-b border-[var(--bdr)] px-[18px] py-[14px]' : '', className]
59
+ .filter(Boolean)
60
+ .join(' ')}
61
+ {...rest}
62
+ >
53
63
  {children}
54
64
  </div>
55
65
  )
@@ -62,7 +72,7 @@ export function CardTitle({
62
72
  }: HTMLAttributes<HTMLHeadingElement>) {
63
73
  return (
64
74
  <h3
65
- className={`text-lg leading-tight font-medium tracking-tight text-[var(--card-foreground)] ${className}`}
75
+ className={`text-[length:var(--fs-card)] font-semibold text-[var(--txt)] ${className}`}
66
76
  {...rest}
67
77
  >
68
78
  {children}
@@ -76,15 +86,19 @@ export function CardDescription({
76
86
  ...rest
77
87
  }: HTMLAttributes<HTMLParagraphElement>) {
78
88
  return (
79
- <p className={`text-sm text-[var(--muted-foreground)] ${className}`} {...rest}>
89
+ <p className={`mt-px text-[length:var(--fs-hint)] text-[var(--muted)] ${className}`} {...rest}>
80
90
  {children}
81
91
  </p>
82
92
  )
83
93
  }
84
94
 
95
+ /** Body slot — `.dcard-b` in the design system handoff. */
85
96
  export function CardContent({ className = '', children, ...rest }: HTMLAttributes<HTMLDivElement>) {
86
97
  return (
87
- <div className={className} {...rest}>
98
+ <div
99
+ className={`px-[18px] py-4 text-[length:var(--fs-body)] leading-[1.55] text-[var(--sub)] ${className}`}
100
+ {...rest}
101
+ >
88
102
  {children}
89
103
  </div>
90
104
  )
@@ -92,7 +106,10 @@ export function CardContent({ className = '', children, ...rest }: HTMLAttribute
92
106
 
93
107
  export function CardFooter({ className = '', children, ...rest }: HTMLAttributes<HTMLDivElement>) {
94
108
  return (
95
- <div className={`flex items-center justify-end gap-2 pt-4 ${className}`} {...rest}>
109
+ <div
110
+ className={`flex items-center justify-end gap-2 border-t border-[var(--bdr)] px-[18px] py-3 ${className}`}
111
+ {...rest}
112
+ >
96
113
  {children}
97
114
  </div>
98
115
  )
@@ -1,24 +1,22 @@
1
1
  import { forwardRef, type InputHTMLAttributes } from 'react'
2
2
  import { cv, type VariantProps } from '../../lib/cv.js'
3
+ import { formControlBase, formControlState } from './form-control.js'
3
4
 
4
- const input = cv(
5
- 'focus-visible:ring-offset-background flex w-full rounded-[var(--radius)] border bg-[var(--background)] text-[var(--foreground)] file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[var(--muted-foreground)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
6
- {
7
- variants: {
8
- size: {
9
- sm: 'h-8 px-2.5 text-xs',
10
- md: 'h-9 px-3 text-sm',
11
- lg: 'h-10 px-4 text-base',
12
- },
13
- state: {
14
- default: 'border-[var(--border)]',
15
- invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
16
- valid: 'border-emerald-500 focus-visible:ring-emerald-500',
17
- },
5
+ const input = cv(formControlBase, {
6
+ variants: {
7
+ size: {
8
+ sm: 'py-1.5 text-[length:var(--fs-sub)]',
9
+ md: '',
10
+ lg: 'py-2.5 text-[length:var(--fs-body)]',
11
+ },
12
+ state: {
13
+ default: formControlState.default,
14
+ invalid: formControlState.invalid,
15
+ valid: formControlState.valid,
18
16
  },
19
- defaultVariants: { size: 'md', state: 'default' },
20
17
  },
21
- )
18
+ defaultVariants: { size: 'md', state: 'default' },
19
+ })
22
20
 
23
21
  export type InputVariants = VariantProps<typeof input>
24
22
 
@@ -32,4 +30,4 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
32
30
  return <input ref={ref} className={input({ size, state, class: className })} {...rest} />
33
31
  })
34
32
 
35
- export { input as inputVariants }
33
+ export { input as inputVariants, formControlBase, formControlState }
@@ -1,5 +1,7 @@
1
1
  'use client'
2
2
 
3
+ import { inputVariants } from './Input.js'
4
+
3
5
  export interface SearchInputProps {
4
6
  value: string
5
7
  onChange: (value: string) => void
@@ -10,11 +12,12 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
10
12
  return (
11
13
  <div className="relative flex-1">
12
14
  <svg
13
- className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]"
15
+ className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--muted)]"
14
16
  fill="none"
15
17
  viewBox="0 0 24 24"
16
18
  stroke="currentColor"
17
19
  strokeWidth={2}
20
+ aria-hidden
18
21
  >
19
22
  <path
20
23
  strokeLinecap="round"
@@ -27,12 +30,13 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
27
30
  value={value}
28
31
  onChange={(e) => onChange(e.target.value)}
29
32
  placeholder={placeholder}
30
- className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] py-2 pr-8 pl-9 text-sm outline-none focus:ring-2 focus:ring-[var(--ring)]"
33
+ className={inputVariants({ class: 'py-2 pr-8 pl-9' })}
31
34
  />
32
35
  {value && (
33
36
  <button
37
+ type="button"
34
38
  onClick={() => onChange('')}
35
- className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
39
+ className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-[var(--muted)] hover:text-[var(--txt)]"
36
40
  aria-label="Clear search"
37
41
  >
38
42
  <svg
@@ -41,12 +45,13 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
41
45
  viewBox="0 0 24 24"
42
46
  stroke="currentColor"
43
47
  strokeWidth={2}
48
+ aria-hidden
44
49
  >
45
50
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
46
51
  </svg>
47
52
  </button>
48
53
  )}
49
- <kbd className="pointer-events-none absolute top-1/2 right-8 hidden -translate-y-1/2 rounded border border-[var(--border)] px-1.5 py-0.5 text-[10px] text-[var(--muted-foreground)] sm:inline-block">
54
+ <kbd className="pointer-events-none absolute top-1/2 right-8 hidden -translate-y-1/2 rounded border border-[var(--bdr)] px-1.5 py-0.5 text-[10px] text-[var(--muted)] sm:inline-block">
50
55
  ⌘K
51
56
  </kbd>
52
57
  </div>
@@ -1,25 +1,22 @@
1
1
  import { forwardRef, type SelectHTMLAttributes, type ReactNode } from 'react'
2
2
  import { cv, type VariantProps } from '../../lib/cv.js'
3
+ import { formControlBase, formControlState } from './form-control.js'
3
4
 
4
- const select = cv(
5
- // Shares geometry with Input so they line up in forms.
6
- 'focus-visible:ring-offset-background flex w-full appearance-none rounded-[var(--radius)] border bg-[var(--background)] pr-8 text-[var(--foreground)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
7
- {
8
- variants: {
9
- size: {
10
- sm: 'h-8 pr-7 pl-2.5 text-xs',
11
- md: 'h-9 pr-8 pl-3 text-sm',
12
- lg: 'h-10 pr-9 pl-4 text-base',
13
- },
14
- state: {
15
- default: 'border-[var(--border)]',
16
- invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
17
- valid: 'border-emerald-500 focus-visible:ring-emerald-500',
18
- },
5
+ const select = cv(`${formControlBase} appearance-none pr-8`, {
6
+ variants: {
7
+ size: {
8
+ sm: 'py-1.5 pr-7 pl-3 text-[length:var(--fs-sub)]',
9
+ md: '',
10
+ lg: 'py-2.5 pr-9 pl-3',
11
+ },
12
+ state: {
13
+ default: formControlState.default,
14
+ invalid: formControlState.invalid,
15
+ valid: formControlState.valid,
19
16
  },
20
- defaultVariants: { size: 'md', state: 'default' },
21
17
  },
22
- )
18
+ defaultVariants: { size: 'md', state: 'default' },
19
+ })
23
20
 
24
21
  export type SelectVariants = VariantProps<typeof select>
25
22
 
@@ -39,7 +36,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select
39
36
  </select>
40
37
  <svg
41
38
  aria-hidden
42
- className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]"
39
+ className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-[var(--muted)]"
43
40
  viewBox="0 0 20 20"
44
41
  fill="currentColor"
45
42
  >
@@ -0,0 +1,85 @@
1
+ 'use client'
2
+
3
+ import * as Switch from '@radix-ui/react-switch'
4
+ import { cv, type VariantProps } from '../../lib/cv.js'
5
+
6
+ const toggleTrack = cv(
7
+ 'relative inline-flex shrink-0 cursor-pointer rounded-[var(--r-pill)] transition-colors duration-[var(--motion-base)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ /** Design system default — accent when on. */
12
+ accent: 'bg-[var(--bdr)] data-[state=checked]:bg-[var(--acc)]',
13
+ /** Semantic enable/disable — success green when on (tables, script tags). */
14
+ enabled: 'bg-[var(--bdr)] data-[state=checked]:bg-[var(--suc)]',
15
+ },
16
+ size: {
17
+ /** Matches `.tog-track` in the design system handoff (38×21). */
18
+ md: 'h-[21px] w-[38px]',
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ variant: 'accent',
23
+ size: 'md',
24
+ },
25
+ },
26
+ )
27
+
28
+ const toggleThumb = cv(
29
+ 'pointer-events-none block rounded-full bg-white shadow-[0_1px_3px_rgba(0,0,0,0.2)] transition-transform duration-[var(--motion-base)]',
30
+ {
31
+ variants: {
32
+ size: {
33
+ md: 'h-4 w-4 translate-x-0.5 data-[state=checked]:translate-x-[17.5px]',
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ size: 'md',
38
+ },
39
+ },
40
+ )
41
+
42
+ export type ToggleVariants = VariantProps<typeof toggleTrack>
43
+
44
+ export interface ToggleProps extends ToggleVariants {
45
+ checked: boolean
46
+ onCheckedChange: (checked: boolean) => void
47
+ disabled?: boolean
48
+ id?: string
49
+ 'aria-label'?: string
50
+ 'aria-labelledby'?: string
51
+ 'aria-describedby'?: string
52
+ }
53
+
54
+ /**
55
+ * Design-system toggle (`.tog-track` / `.tog-thumb`). Use `variant="enabled"`
56
+ * for on/off tables where green communicates active state.
57
+ */
58
+ export function Toggle({
59
+ checked,
60
+ onCheckedChange,
61
+ disabled,
62
+ variant,
63
+ size,
64
+ id,
65
+ 'aria-label': ariaLabel,
66
+ 'aria-labelledby': ariaLabelledBy,
67
+ 'aria-describedby': ariaDescribedBy,
68
+ }: ToggleProps) {
69
+ return (
70
+ <Switch.Root
71
+ id={id}
72
+ checked={checked}
73
+ onCheckedChange={onCheckedChange}
74
+ disabled={disabled}
75
+ aria-label={ariaLabel}
76
+ aria-labelledby={ariaLabelledBy}
77
+ aria-describedby={ariaDescribedBy}
78
+ className={toggleTrack({ variant, size })}
79
+ >
80
+ <Switch.Thumb className={toggleThumb({ size })} />
81
+ </Switch.Root>
82
+ )
83
+ }
84
+
85
+ export { toggleTrack as toggleTrackVariants, toggleThumb as toggleThumbVariants }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared form-control surface styles (`.inp` in the design system handoff).
3
+ * Imported by Input and Select so geometry stays aligned.
4
+ */
5
+ export const formControlBase =
6
+ 'w-full rounded-[var(--r-control)] border border-[var(--bdr)] bg-[var(--bg)] px-3 py-[9px] text-[length:var(--fs-body)] text-[var(--txt)] outline-none transition-[border-color] duration-150 placeholder:text-[var(--muted)] focus:border-[var(--acc-m)] focus-visible:border-[var(--acc-m)] disabled:cursor-not-allowed disabled:opacity-50'
7
+
8
+ export const formControlState = {
9
+ default: '',
10
+ invalid: 'border-[var(--err)] focus:border-[var(--err)] focus-visible:border-[var(--err)]',
11
+ valid: 'border-[var(--suc)] focus:border-[var(--suc)] focus-visible:border-[var(--suc)]',
12
+ } as const
@@ -34,5 +34,8 @@ export { Pagination } from './Pagination.js'
34
34
  export type { PaginationProps } from './Pagination.js'
35
35
  export { SearchInput } from './SearchInput.js'
36
36
  export type { SearchInputProps } from './SearchInput.js'
37
+ export { Toggle, toggleTrackVariants, toggleThumbVariants } from './Toggle.js'
38
+ export type { ToggleProps, ToggleVariants } from './Toggle.js'
39
+ export { formControlBase, formControlState } from './form-control.js'
37
40
  export { CommandPalette } from './CommandPalette.js'
38
41
  export type { CommandPaletteProps } from './CommandPalette.js'
@@ -8,6 +8,7 @@ import { CommandPalette } from '../components/CommandPalette.js'
8
8
  import { NotificationBell } from '../components/NotificationBell.js'
9
9
  import { useTheme } from '../components/ThemeProvider.js'
10
10
  import { LocaleSwitcher } from '../components/LocaleSwitcher.js'
11
+ import { inputVariants } from '../components/ui/Input.js'
11
12
 
12
13
  export interface HeaderProps {
13
14
  onToggleSidebar: () => void
@@ -51,7 +52,7 @@ export function Header({
51
52
 
52
53
  return (
53
54
  <>
54
- <header className="border-border bg-background flex h-14 items-center gap-3 border-b px-4">
55
+ <header className="border-sidebar-border bg-sidebar flex h-[var(--tbh)] items-center gap-3 border-b px-4">
55
56
  <button
56
57
  onClick={onToggleSidebar}
57
58
  className="hover:bg-accent rounded-lg p-2 transition-colors md:hidden"
@@ -77,7 +78,10 @@ export function Header({
77
78
  onKeyDown={(e) => {
78
79
  if (e.key === 'Escape') (e.target as HTMLInputElement).blur()
79
80
  }}
80
- className="border-border bg-input-background text-foreground placeholder:text-muted-foreground focus:ring-ring h-9 w-full max-w-md rounded-lg border py-1.5 pr-14 pl-9 text-sm focus:border-transparent focus:ring-2 focus:outline-none"
81
+ className={inputVariants({
82
+ class:
83
+ 'h-9 w-full max-w-md py-1.5 pr-14 pl-9 focus-visible:ring-0 focus-visible:ring-offset-0',
84
+ })}
81
85
  />
82
86
  {/* Keyboard hint pill. Hidden on narrow widths to avoid overlap. */}
83
87
  <span
@@ -253,13 +253,13 @@ export function Sidebar({
253
253
 
254
254
  return (
255
255
  <aside
256
- className={`bg-sidebar border-sidebar-border flex h-full flex-col border-r transition-all duration-200 ${
257
- collapsed ? 'w-20' : 'w-64'
256
+ className={`bg-sidebar border-sidebar-border flex h-full flex-col border-r transition-[width] duration-[var(--motion-layout)] ${
257
+ collapsed ? 'w-[var(--sbw-col)]' : 'w-[var(--sbw)]'
258
258
  }`}
259
259
  >
260
260
  {/* Brand header — no toggle button here anymore; it now lives in the footer. */}
261
261
  <div
262
- className={`border-sidebar-border flex h-14 shrink-0 items-center border-b px-4 ${
262
+ className={`border-sidebar-border flex h-[var(--tbh)] shrink-0 items-center border-b px-4 ${
263
263
  collapsed ? 'justify-center' : ''
264
264
  }`}
265
265
  >
@@ -49,13 +49,20 @@ export const tokens = {
49
49
 
50
50
  radius: {
51
51
  none: 0,
52
- sm: 2,
53
- md: 6,
54
- lg: 8,
52
+ sm: 6,
53
+ md: 8,
54
+ lg: 10,
55
55
  xl: 12,
56
- full: 9999,
56
+ full: 99,
57
57
  } as const satisfies Record<RadiusToken, number>,
58
58
 
59
+ /** Layout dimensions from the design system (px). */
60
+ layout: {
61
+ sidebarWidth: 224,
62
+ sidebarWidthCollapsed: 60,
63
+ topbarHeight: 56,
64
+ } as const,
65
+
59
66
  // Breakpoints mirror Tailwind defaults so primitives can compose with
60
67
  // utility classes without surprises. Use `matchMedia(`(min-width: …`)` to
61
68
  // sync JS-driven layout decisions with these values.
@@ -0,0 +1,97 @@
1
+ /*
2
+ * ActuateCMS design tokens — scoped to the admin root.
3
+ * Source: design-system/tokens.json (v1.0).
4
+ * Appearance accent overrides set --acc / --primary at runtime.
5
+ */
6
+
7
+ .actuate-admin {
8
+ /* Surfaces */
9
+ --bg: #f5f4fa;
10
+ --sb: #ffffff;
11
+ --card: #ffffff;
12
+ --bdr: #e8e6f2;
13
+
14
+ /* Text */
15
+ --txt: #1a1827;
16
+ --sub: #6b6878;
17
+ --muted: #b0adbe;
18
+
19
+ /* Accent */
20
+ --acc: #7c3aed;
21
+ --acc-l: #ede9fe;
22
+ --acc-m: #c4b5fd;
23
+
24
+ /* Semantic */
25
+ --suc: #059669;
26
+ --suc-l: #ecfdf5;
27
+ --warn: #d97706;
28
+ --warn-l: #fffbeb;
29
+ --err: #dc2626;
30
+ --err-l: #fef2f2;
31
+ --info: #0891b2;
32
+ --info-l: #e0f7fa;
33
+
34
+ /* AI */
35
+ --ai1: #7c3aed;
36
+ --ai2: #c026d3;
37
+ --ai-soft: linear-gradient(135deg, rgba(124, 58, 237, 0.09), rgba(192, 38, 211, 0.07));
38
+
39
+ /* Layout */
40
+ --sbw: 224px;
41
+ --sbw-col: 60px;
42
+ --tbh: 56px;
43
+ --r: 10px;
44
+ --r-control: 8px;
45
+ --r-sm: 6px;
46
+ --r-pill: 99px;
47
+
48
+ /* Elevation */
49
+ --shd: 0 1px 2px rgba(0, 0, 0, 0.05), 0 1px 4px rgba(0, 0, 0, 0.04);
50
+ --shd2: 0 4px 16px rgba(0, 0, 0, 0.1);
51
+
52
+ /* Typography */
53
+ --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
54
+ --font-mono: 'DM Mono', ui-monospace, 'SFMono-Regular', monospace;
55
+ --fs-page: 21px;
56
+ --fs-card: 14px;
57
+ --fs-body: 13px;
58
+ --fs-sub: 12px;
59
+ --fs-hint: 11.5px;
60
+ --fs-label: 10px;
61
+
62
+ /* Motion */
63
+ --motion-fast: 0.12s;
64
+ --motion-base: 0.18s;
65
+ --motion-layout: 0.22s cubic-bezier(0.4, 0, 0.2, 1);
66
+ }
67
+
68
+ .dark .actuate-admin,
69
+ .actuate-admin.dark,
70
+ .actuate-admin[data-theme='dark'] {
71
+ --bg: #0d0c18;
72
+ --sb: #141222;
73
+ --card: #1c1a2e;
74
+ --bdr: #2c2a42;
75
+
76
+ --txt: #eae8f5;
77
+ --sub: #8e8ba8;
78
+ --muted: #504e68;
79
+
80
+ --acc: #9d71f0;
81
+ --acc-l: #231950;
82
+ --acc-m: #5e3eaa;
83
+
84
+ --suc: #10b981;
85
+ --suc-l: #022c22;
86
+ --warn: #fbbf24;
87
+ --warn-l: #1a1000;
88
+ --err: #f87171;
89
+ --err-l: #200808;
90
+ --info: #38bdf8;
91
+ --info-l: #0a2838;
92
+
93
+ --ai-soft: linear-gradient(135deg, rgba(157, 113, 240, 0.16), rgba(192, 38, 211, 0.12));
94
+
95
+ --shd: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2);
96
+ --shd2: 0 4px 20px rgba(0, 0, 0, 0.45);
97
+ }