@arqel-dev/theme 0.15.0 → 0.16.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.
- package/SKILL.md +3 -3
- package/dist/index.js +9 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
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
|
-
```
|
|
56
|
-
<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. **
|
|
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
|
|
37
|
-
system: "
|
|
38
|
-
light: "
|
|
39
|
-
dark: "
|
|
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":
|
|
111
|
-
title:
|
|
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":"
|
|
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.
|
|
3
|
+
"version": "0.16.0",
|
|
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.
|
|
38
|
+
"@arqel-dev/react": "0.16.0"
|
|
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",
|