@arqel-dev/theme 0.14.0 → 0.15.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.
package/SKILL.md CHANGED
@@ -52,8 +52,8 @@ Lança erro se chamado fora de `<ThemeProvider>` (fail-fast).
52
52
 
53
53
  Retorna string IIFE para inserir antes dos bundles React:
54
54
 
55
- ```blade
56
- <script>{!! \Arqel\Theme\preventFlashScript() !!}</script>
55
+ ```tsx
56
+ <script dangerouslySetInnerHTML={{ __html: preventFlashScript() }} />
57
57
  ```
58
58
 
59
59
  ## Conventions
@@ -61,7 +61,7 @@ Retorna string IIFE para inserir antes dos bundles React:
61
61
  1. **Tokens semânticos, nunca cores cruas** — componentes Arqel usam `var(--arqel-color-bg)`, nunca `#ffffff`.
62
62
  2. **localStorage opt-out** — falhas de `localStorage.setItem/getItem` são silenciadas (Safari private mode, iframes).
63
63
  3. **SSR-safe** — todas funções verificam `typeof window === 'undefined'`.
64
- 4. **Sem dependências runtime extras** — apenas `react` (peer).
64
+ 4. **Depende de `@arqel-dev/react` em runtime** — `ThemeProvider`/`useTheme`/`ThemeContext` são re-exportados de `@arqel-dev/react/providers` (contexto único, issue #236) e `ThemeToggle` usa `useArqelTranslations` de `@arqel-dev/react/utils`. `react` é peer dependency.
65
65
  5. **Class-based dark mode** (Tailwind v4 syntax: `@variant dark (&:where(.dark, .dark *))`).
66
66
 
67
67
  ## Examples
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useTheme } from '@arqel-dev/react/providers';
2
2
  export { ThemeContext, ThemeProvider, preventFlashScript, useTheme } from '@arqel-dev/react/providers';
3
+ import { useArqelTranslations } from '@arqel-dev/react/utils';
3
4
  import { jsx, jsxs } from 'react/jsx-runtime';
4
5
 
5
6
  // src/preventFlash.ts
@@ -33,10 +34,10 @@ function getSystemTheme() {
33
34
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
34
35
  }
35
36
  var CYCLE = ["system", "light", "dark"];
36
- var LABELS = {
37
- system: "Tema: sistema (clique para claro)",
38
- light: "Tema: claro (clique para escuro)",
39
- dark: "Tema: escuro (clique para sistema)"
37
+ var LABEL_FALLBACKS = {
38
+ system: "Theme: system (click for light)",
39
+ light: "Theme: light (click for dark)",
40
+ dark: "Theme: dark (click for system)"
40
41
  };
41
42
  function nextTheme(current) {
42
43
  const idx = CYCLE.indexOf(current);
@@ -101,14 +102,16 @@ function MonitorIcon() {
101
102
  }
102
103
  function ThemeToggle({ className } = {}) {
103
104
  const { theme, setTheme } = useTheme();
105
+ const t = useArqelTranslations();
104
106
  const icon = theme === "light" ? /* @__PURE__ */ jsx(SunIcon, {}) : theme === "dark" ? /* @__PURE__ */ jsx(MoonIcon, {}) : /* @__PURE__ */ jsx(MonitorIcon, {});
107
+ const label = t(`arqel.theme.toggle.${theme}`, LABEL_FALLBACKS[theme]);
105
108
  return /* @__PURE__ */ jsx(
106
109
  "button",
107
110
  {
108
111
  type: "button",
109
112
  onClick: () => setTheme(nextTheme(theme)),
110
- "aria-label": LABELS[theme],
111
- title: LABELS[theme],
113
+ "aria-label": label,
114
+ title: label,
112
115
  "data-theme-current": theme,
113
116
  className,
114
117
  children: icon
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage.ts","../src/ThemeToggle.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Theme } from './types';\n\nexport const DEFAULT_STORAGE_KEY = 'arqel-theme';\n\nconst VALID_THEMES: readonly Theme[] = ['light', 'dark', 'system'];\n\nexport function isTheme(value: unknown): value is Theme {\n return typeof value === 'string' && (VALID_THEMES as readonly string[]).includes(value);\n}\n\n/**\n * Lê preferência do localStorage. SSR-safe: retorna `null` no servidor.\n *\n * @deprecated Após a unificação de contexto (issue #236), o `ThemeProvider`\n * passou a gerenciar a leitura do `localStorage` internamente. Ler a chave\n * por fora não reflete o estado do provider. Prefira `useTheme().theme`.\n */\nexport function readStoredTheme(key: string = DEFAULT_STORAGE_KEY): Theme | null {\n if (typeof window === 'undefined') return null;\n try {\n const raw = window.localStorage.getItem(key);\n return isTheme(raw) ? raw : null;\n } catch {\n // localStorage pode estar bloqueado (Safari private, iframes, etc).\n return null;\n }\n}\n\n/**\n * Persiste preferência em localStorage. Silencioso em erro.\n *\n * @deprecated Após a unificação de contexto (issue #236), o `ThemeProvider`\n * é a única fonte de escrita do `localStorage`. Chamar isto em runtime grava\n * a chave mas NÃO atualiza o estado do provider. Prefira `useTheme().setTheme`.\n */\nexport function writeStoredTheme(theme: Theme, key: string = DEFAULT_STORAGE_KEY): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(key, theme);\n } catch {\n // ignore\n }\n}\n\n/**\n * Detecta system preference. SSR-safe: retorna `light` no servidor.\n */\nexport function getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return 'light';\n }\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n","import type { ReactNode } from 'react';\nimport type { Theme } from './types';\nimport { useTheme } from './useTheme';\n\nconst CYCLE: readonly Theme[] = ['system', 'light', 'dark'];\n\nconst LABELS: Record<Theme, string> = {\n system: 'Tema: sistema (clique para claro)',\n light: 'Tema: claro (clique para escuro)',\n dark: 'Tema: escuro (clique para sistema)',\n};\n\nfunction nextTheme(current: Theme): Theme {\n const idx = CYCLE.indexOf(current);\n return CYCLE[(idx + 1) % CYCLE.length] ?? 'system';\n}\n\nfunction SunIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\" />\n </svg>\n );\n}\n\nfunction MoonIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n );\n}\n\nfunction MonitorIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M8 21h8M12 17v4\" />\n </svg>\n );\n}\n\nexport interface ThemeToggleProps {\n className?: string;\n}\n\n/**\n * Botão que cicla entre `system → light → dark → system`.\n * Mostra ícone correspondente ao **tema atualmente selecionado**\n * (não ao próximo) e usa `aria-label` descritivo para acessibilidade.\n */\nexport function ThemeToggle({ className }: ThemeToggleProps = {}): ReactNode {\n const { theme, setTheme } = useTheme();\n\n const icon = theme === 'light' ? <SunIcon /> : theme === 'dark' ? <MoonIcon /> : <MonitorIcon />;\n\n return (\n <button\n type=\"button\"\n onClick={() => setTheme(nextTheme(theme))}\n aria-label={LABELS[theme]}\n title={LABELS[theme]}\n data-theme-current={theme}\n className={className}\n >\n {icon}\n </button>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/storage.ts","../src/ThemeToggle.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Theme } from './types';\n\nexport const DEFAULT_STORAGE_KEY = 'arqel-theme';\n\nconst VALID_THEMES: readonly Theme[] = ['light', 'dark', 'system'];\n\nexport function isTheme(value: unknown): value is Theme {\n return typeof value === 'string' && (VALID_THEMES as readonly string[]).includes(value);\n}\n\n/**\n * Lê preferência do localStorage. SSR-safe: retorna `null` no servidor.\n *\n * @deprecated Após a unificação de contexto (issue #236), o `ThemeProvider`\n * passou a gerenciar a leitura do `localStorage` internamente. Ler a chave\n * por fora não reflete o estado do provider. Prefira `useTheme().theme`.\n */\nexport function readStoredTheme(key: string = DEFAULT_STORAGE_KEY): Theme | null {\n if (typeof window === 'undefined') return null;\n try {\n const raw = window.localStorage.getItem(key);\n return isTheme(raw) ? raw : null;\n } catch {\n // localStorage pode estar bloqueado (Safari private, iframes, etc).\n return null;\n }\n}\n\n/**\n * Persiste preferência em localStorage. Silencioso em erro.\n *\n * @deprecated Após a unificação de contexto (issue #236), o `ThemeProvider`\n * é a única fonte de escrita do `localStorage`. Chamar isto em runtime grava\n * a chave mas NÃO atualiza o estado do provider. Prefira `useTheme().setTheme`.\n */\nexport function writeStoredTheme(theme: Theme, key: string = DEFAULT_STORAGE_KEY): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(key, theme);\n } catch {\n // ignore\n }\n}\n\n/**\n * Detecta system preference. SSR-safe: retorna `light` no servidor.\n */\nexport function getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return 'light';\n }\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n","import { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ReactNode } from 'react';\nimport type { Theme } from './types';\nimport { useTheme } from './useTheme';\n\nconst CYCLE: readonly Theme[] = ['system', 'light', 'dark'];\n\n/**\n * English fallbacks for each theme's aria-label/title. The localized values\n * live in `arqel.theme.toggle.*` (resolved client-side); these literals are\n * rendered when the dictionary is absent so the accessible name stays stable.\n */\nconst LABEL_FALLBACKS: Record<Theme, string> = {\n system: 'Theme: system (click for light)',\n light: 'Theme: light (click for dark)',\n dark: 'Theme: dark (click for system)',\n};\n\nfunction nextTheme(current: Theme): Theme {\n const idx = CYCLE.indexOf(current);\n return CYCLE[(idx + 1) % CYCLE.length] ?? 'system';\n}\n\nfunction SunIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41\" />\n </svg>\n );\n}\n\nfunction MoonIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\" />\n </svg>\n );\n}\n\nfunction MonitorIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M8 21h8M12 17v4\" />\n </svg>\n );\n}\n\nexport interface ThemeToggleProps {\n className?: string;\n}\n\n/**\n * Botão que cicla entre `system → light → dark → system`.\n * Mostra ícone correspondente ao **tema atualmente selecionado**\n * (não ao próximo) e usa `aria-label` descritivo para acessibilidade.\n */\nexport function ThemeToggle({ className }: ThemeToggleProps = {}): ReactNode {\n const { theme, setTheme } = useTheme();\n const t = useArqelTranslations();\n\n const icon = theme === 'light' ? <SunIcon /> : theme === 'dark' ? <MoonIcon /> : <MonitorIcon />;\n const label = t(`arqel.theme.toggle.${theme}`, LABEL_FALLBACKS[theme]);\n\n return (\n <button\n type=\"button\"\n onClick={() => setTheme(nextTheme(theme))}\n aria-label={label}\n title={label}\n data-theme-current={theme}\n className={className}\n >\n {icon}\n </button>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arqel-dev/theme",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "Tokens semânticos + dark-mode + ThemeProvider/ThemeToggle para painéis Arqel.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://arqel.dev",
@@ -35,12 +35,13 @@
35
35
  "SKILL.md"
36
36
  ],
37
37
  "dependencies": {
38
- "@arqel-dev/react": "0.14.0"
38
+ "@arqel-dev/react": "0.15.1"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "react": "^19.2.0"
42
42
  },
43
43
  "devDependencies": {
44
+ "@inertiajs/react": "^3.0.0",
44
45
  "@testing-library/react": "^16.1.0",
45
46
  "@types/react": "^19.0.0",
46
47
  "@types/react-dom": "^19.0.0",