torch-glare 2.5.5 → 2.6.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/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -338
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -317
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -383
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -84
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -63
- package/apps/lib/components/DataViews/states.tsx +0 -38
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -420
- package/apps/lib/components/DropdownMenu.tsx +0 -540
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -73
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -163
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -91
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -47
- package/apps/lib/components/FormBuilder/types.ts +0 -311
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -128
- package/apps/lib/components/FormRenderer/detail.tsx +0 -207
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -297
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -15
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -339
- package/apps/lib/components/FormRenderer/types.ts +0 -78
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -147
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -253
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -610
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -58
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -214
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1201
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -712
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1495
- package/docs/components/data-views/migration.md +0 -79
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -276
- package/docs/components/form-renderer.md +0 -326
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -477
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -54
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1500
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -704
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,866 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Providers Reference
|
|
3
|
-
description: React context providers for global state management including theme management with light/dark modes and CSS/TORCH theme systems.
|
|
4
|
-
component: Providers
|
|
5
|
-
category: Reference
|
|
6
|
-
tags: [providers, context, theme, dark-mode, light-mode, state-management]
|
|
7
|
-
related:
|
|
8
|
-
- All Components
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Providers
|
|
12
|
-
|
|
13
|
-
React context providers that manage global application state. All providers follow React context best practices and include TypeScript support.
|
|
14
|
-
|
|
15
|
-
## Available Providers
|
|
16
|
-
|
|
17
|
-
- **ThemeProvider** - Manages theme state (light/dark) and theme mode (CSS/TORCH) with localStorage persistence
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## ThemeProvider
|
|
22
|
-
|
|
23
|
-
Manages application theme state with support for light, dark, and system default themes. Includes two theme mode systems (CSS and TORCH) and automatic localStorage persistence.
|
|
24
|
-
|
|
25
|
-
### Features
|
|
26
|
-
|
|
27
|
-
- Light, dark, and default (system) theme support
|
|
28
|
-
- CSS and TORCH theme mode systems
|
|
29
|
-
- Automatic localStorage persistence
|
|
30
|
-
- SSR-safe initialization
|
|
31
|
-
- TypeScript support
|
|
32
|
-
- Custom hook (`useTheme`) for consuming theme state
|
|
33
|
-
|
|
34
|
-
### Basic Setup
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
38
|
-
|
|
39
|
-
function App() {
|
|
40
|
-
return (
|
|
41
|
-
<ThemeProvider>
|
|
42
|
-
<YourApp />
|
|
43
|
-
</ThemeProvider>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### With Default Theme
|
|
49
|
-
|
|
50
|
-
```tsx
|
|
51
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
52
|
-
|
|
53
|
-
function App() {
|
|
54
|
-
return (
|
|
55
|
-
<ThemeProvider defaultTheme="dark" defaultThemeMode="CSS">
|
|
56
|
-
<YourApp />
|
|
57
|
-
</ThemeProvider>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Consuming Theme with useTheme Hook
|
|
63
|
-
|
|
64
|
-
```tsx
|
|
65
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
66
|
-
|
|
67
|
-
function ThemeToggle() {
|
|
68
|
-
const { theme, updateTheme } = useTheme();
|
|
69
|
-
|
|
70
|
-
const toggleTheme = () => {
|
|
71
|
-
updateTheme(theme === 'light' ? 'dark' : 'light');
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<button onClick={toggleTheme}>
|
|
76
|
-
Current theme: {theme}
|
|
77
|
-
Toggle to {theme === 'light' ? 'dark' : 'light'}
|
|
78
|
-
</button>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Theme Mode Switcher
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
87
|
-
|
|
88
|
-
function ThemeModeSwitcher() {
|
|
89
|
-
const { themeMode, updateMode } = useTheme();
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<div>
|
|
93
|
-
<p>Current mode: {themeMode}</p>
|
|
94
|
-
<button onClick={() => updateMode('CSS')}>
|
|
95
|
-
Use CSS Theme
|
|
96
|
-
</button>
|
|
97
|
-
<button onClick={() => updateMode('TORCH')}>
|
|
98
|
-
Use TORCH Theme
|
|
99
|
-
</button>
|
|
100
|
-
</div>
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Complete Theme Control Panel
|
|
106
|
-
|
|
107
|
-
```tsx
|
|
108
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
109
|
-
|
|
110
|
-
function ThemeControlPanel() {
|
|
111
|
-
const { theme, themeMode, updateTheme, updateMode } = useTheme();
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<div className="theme-control-panel">
|
|
115
|
-
<section>
|
|
116
|
-
<h3>Theme</h3>
|
|
117
|
-
<div className="button-group">
|
|
118
|
-
<button
|
|
119
|
-
onClick={() => updateTheme('light')}
|
|
120
|
-
className={theme === 'light' ? 'active' : ''}
|
|
121
|
-
>
|
|
122
|
-
Light
|
|
123
|
-
</button>
|
|
124
|
-
<button
|
|
125
|
-
onClick={() => updateTheme('dark')}
|
|
126
|
-
className={theme === 'dark' ? 'active' : ''}
|
|
127
|
-
>
|
|
128
|
-
Dark
|
|
129
|
-
</button>
|
|
130
|
-
<button
|
|
131
|
-
onClick={() => updateTheme('default')}
|
|
132
|
-
className={theme === 'default' ? 'active' : ''}
|
|
133
|
-
>
|
|
134
|
-
System
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
</section>
|
|
138
|
-
|
|
139
|
-
<section>
|
|
140
|
-
<h3>Theme Mode</h3>
|
|
141
|
-
<div className="button-group">
|
|
142
|
-
<button
|
|
143
|
-
onClick={() => updateMode('TORCH')}
|
|
144
|
-
className={themeMode === 'TORCH' ? 'active' : ''}
|
|
145
|
-
>
|
|
146
|
-
TORCH
|
|
147
|
-
</button>
|
|
148
|
-
<button
|
|
149
|
-
onClick={() => updateMode('CSS')}
|
|
150
|
-
className={themeMode === 'CSS' ? 'active' : ''}
|
|
151
|
-
>
|
|
152
|
-
CSS
|
|
153
|
-
</button>
|
|
154
|
-
</div>
|
|
155
|
-
</section>
|
|
156
|
-
|
|
157
|
-
<div className="current-settings">
|
|
158
|
-
<p>Theme: {theme}</p>
|
|
159
|
-
<p>Mode: {themeMode}</p>
|
|
160
|
-
</div>
|
|
161
|
-
</div>
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Theme-Aware Component
|
|
167
|
-
|
|
168
|
-
```tsx
|
|
169
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
170
|
-
|
|
171
|
-
function ThemedCard({ children }: { children: React.ReactNode }) {
|
|
172
|
-
const { theme } = useTheme();
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<div
|
|
176
|
-
className={cn(
|
|
177
|
-
'card',
|
|
178
|
-
theme === 'dark' && 'bg-gray-900 text-white',
|
|
179
|
-
theme === 'light' && 'bg-white text-gray-900'
|
|
180
|
-
)}
|
|
181
|
-
>
|
|
182
|
-
{children}
|
|
183
|
-
</div>
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### System Theme Detection
|
|
189
|
-
|
|
190
|
-
```tsx
|
|
191
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
192
|
-
import { useEffect, useState } from 'react';
|
|
193
|
-
|
|
194
|
-
function SystemThemeSync() {
|
|
195
|
-
const { theme, updateTheme } = useTheme();
|
|
196
|
-
const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');
|
|
197
|
-
|
|
198
|
-
useEffect(() => {
|
|
199
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
200
|
-
|
|
201
|
-
const handleChange = (e: MediaQueryListEvent) => {
|
|
202
|
-
const newSystemTheme = e.matches ? 'dark' : 'light';
|
|
203
|
-
setSystemTheme(newSystemTheme);
|
|
204
|
-
|
|
205
|
-
// Auto-update if user selected "default"
|
|
206
|
-
if (theme === 'default') {
|
|
207
|
-
updateTheme(newSystemTheme);
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
setSystemTheme(mediaQuery.matches ? 'dark' : 'light');
|
|
212
|
-
mediaQuery.addEventListener('change', handleChange);
|
|
213
|
-
|
|
214
|
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
215
|
-
}, [theme, updateTheme]);
|
|
216
|
-
|
|
217
|
-
return (
|
|
218
|
-
<div>
|
|
219
|
-
<p>System preference: {systemTheme}</p>
|
|
220
|
-
<p>Current theme: {theme}</p>
|
|
221
|
-
{theme === 'default' && (
|
|
222
|
-
<p>Following system preference</p>
|
|
223
|
-
)}
|
|
224
|
-
</div>
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### Header with Theme Toggle
|
|
230
|
-
|
|
231
|
-
```tsx
|
|
232
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
233
|
-
|
|
234
|
-
function Header() {
|
|
235
|
-
const { theme, updateTheme } = useTheme();
|
|
236
|
-
|
|
237
|
-
return (
|
|
238
|
-
<header className="flex justify-between items-center p-4">
|
|
239
|
-
<h1>My App</h1>
|
|
240
|
-
|
|
241
|
-
<button
|
|
242
|
-
onClick={() => updateTheme(theme === 'light' ? 'dark' : 'light')}
|
|
243
|
-
className="theme-toggle"
|
|
244
|
-
aria-label="Toggle theme"
|
|
245
|
-
>
|
|
246
|
-
{theme === 'light' ? '🌙' : '☀️'}
|
|
247
|
-
</button>
|
|
248
|
-
</header>
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Settings Page
|
|
254
|
-
|
|
255
|
-
```tsx
|
|
256
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
257
|
-
import { useState } from 'react';
|
|
258
|
-
|
|
259
|
-
function SettingsPage() {
|
|
260
|
-
const { theme, themeMode, updateTheme, updateMode } = useTheme();
|
|
261
|
-
const [saved, setSaved] = useState(false);
|
|
262
|
-
|
|
263
|
-
const handleSave = () => {
|
|
264
|
-
// Theme is automatically persisted to localStorage
|
|
265
|
-
setSaved(true);
|
|
266
|
-
setTimeout(() => setSaved(false), 2000);
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
return (
|
|
270
|
-
<div className="settings-page">
|
|
271
|
-
<h1>Appearance Settings</h1>
|
|
272
|
-
|
|
273
|
-
<div className="setting-section">
|
|
274
|
-
<label>Theme</label>
|
|
275
|
-
<select
|
|
276
|
-
value={theme}
|
|
277
|
-
onChange={(e) => updateTheme(e.target.value as any)}
|
|
278
|
-
>
|
|
279
|
-
<option value="light">Light</option>
|
|
280
|
-
<option value="dark">Dark</option>
|
|
281
|
-
<option value="default">System Default</option>
|
|
282
|
-
</select>
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
<div className="setting-section">
|
|
286
|
-
<label>Theme System</label>
|
|
287
|
-
<select
|
|
288
|
-
value={themeMode}
|
|
289
|
-
onChange={(e) => updateMode(e.target.value as any)}
|
|
290
|
-
>
|
|
291
|
-
<option value="TORCH">TORCH</option>
|
|
292
|
-
<option value="CSS">CSS</option>
|
|
293
|
-
</select>
|
|
294
|
-
<p className="help-text">
|
|
295
|
-
{themeMode === 'TORCH'
|
|
296
|
-
? 'Using TORCH design tokens'
|
|
297
|
-
: 'Using CSS custom properties'}
|
|
298
|
-
</p>
|
|
299
|
-
</div>
|
|
300
|
-
|
|
301
|
-
<button onClick={handleSave}>
|
|
302
|
-
{saved ? 'Saved!' : 'Save Settings'}
|
|
303
|
-
</button>
|
|
304
|
-
</div>
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### Next.js App Router Setup
|
|
310
|
-
|
|
311
|
-
```tsx
|
|
312
|
-
// app/layout.tsx
|
|
313
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
314
|
-
|
|
315
|
-
export default function RootLayout({
|
|
316
|
-
children,
|
|
317
|
-
}: {
|
|
318
|
-
children: React.ReactNode;
|
|
319
|
-
}) {
|
|
320
|
-
return (
|
|
321
|
-
<html lang="en">
|
|
322
|
-
<body>
|
|
323
|
-
<ThemeProvider defaultTheme="light" defaultThemeMode="TORCH">
|
|
324
|
-
{children}
|
|
325
|
-
</ThemeProvider>
|
|
326
|
-
</body>
|
|
327
|
-
</html>
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### Next.js Pages Router Setup
|
|
333
|
-
|
|
334
|
-
```tsx
|
|
335
|
-
// pages/_app.tsx
|
|
336
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
337
|
-
import type { AppProps } from 'next/app';
|
|
338
|
-
|
|
339
|
-
export default function App({ Component, pageProps }: AppProps) {
|
|
340
|
-
return (
|
|
341
|
-
<ThemeProvider defaultTheme="dark">
|
|
342
|
-
<Component {...pageProps} />
|
|
343
|
-
</ThemeProvider>
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### Vite + React Setup
|
|
349
|
-
|
|
350
|
-
```tsx
|
|
351
|
-
// main.tsx
|
|
352
|
-
import React from 'react';
|
|
353
|
-
import ReactDOM from 'react-dom/client';
|
|
354
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
355
|
-
import App from './App';
|
|
356
|
-
|
|
357
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
358
|
-
<React.StrictMode>
|
|
359
|
-
<ThemeProvider defaultTheme="light" defaultThemeMode="TORCH">
|
|
360
|
-
<App />
|
|
361
|
-
</ThemeProvider>
|
|
362
|
-
</React.StrictMode>
|
|
363
|
-
);
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
## API Reference
|
|
369
|
-
|
|
370
|
-
### ThemeProvider Props
|
|
371
|
-
|
|
372
|
-
```typescript
|
|
373
|
-
interface ThemeProviderProps {
|
|
374
|
-
children: ReactNode;
|
|
375
|
-
defaultTheme?: "light" | "dark" | "default";
|
|
376
|
-
defaultThemeMode?: "CSS" | "TORCH";
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
| Prop | Type | Default | Description |
|
|
381
|
-
|------|------|---------|-------------|
|
|
382
|
-
| `children` | `ReactNode` | Required | Child components to be wrapped |
|
|
383
|
-
| `defaultTheme` | `"light" \| "dark" \| "default"` | `"default"` | Initial theme to use |
|
|
384
|
-
| `defaultThemeMode` | `"CSS" \| "TORCH"` | `"TORCH"` | Initial theme mode system |
|
|
385
|
-
|
|
386
|
-
### useTheme Hook
|
|
387
|
-
|
|
388
|
-
```typescript
|
|
389
|
-
interface ThemeProps {
|
|
390
|
-
theme: "light" | "dark" | "default";
|
|
391
|
-
themeMode: "CSS" | "TORCH";
|
|
392
|
-
updateTheme: (theme: "light" | "dark" | "default") => void;
|
|
393
|
-
updateMode: (themeMode: "CSS" | "TORCH") => void;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function useTheme(): ThemeProps
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
#### Return Value
|
|
400
|
-
|
|
401
|
-
| Property | Type | Description |
|
|
402
|
-
|----------|------|-------------|
|
|
403
|
-
| `theme` | `"light" \| "dark" \| "default"` | Current theme |
|
|
404
|
-
| `themeMode` | `"CSS" \| "TORCH"` | Current theme mode system |
|
|
405
|
-
| `updateTheme` | `(theme: "light" \| "dark" \| "default") => void` | Update the theme |
|
|
406
|
-
| `updateMode` | `(themeMode: "CSS" \| "TORCH") => void` | Update the theme mode |
|
|
407
|
-
|
|
408
|
-
---
|
|
409
|
-
|
|
410
|
-
## Theme Values
|
|
411
|
-
|
|
412
|
-
### Theme Types
|
|
413
|
-
|
|
414
|
-
| Value | Description |
|
|
415
|
-
|-------|-------------|
|
|
416
|
-
| `"light"` | Light theme with bright backgrounds and dark text |
|
|
417
|
-
| `"dark"` | Dark theme with dark backgrounds and light text |
|
|
418
|
-
| `"default"` | Uses system preference (respects `prefers-color-scheme`) |
|
|
419
|
-
|
|
420
|
-
### Theme Modes
|
|
421
|
-
|
|
422
|
-
| Value | Description |
|
|
423
|
-
|-------|-------------|
|
|
424
|
-
| `"TORCH"` | Uses TORCH design token system |
|
|
425
|
-
| `"CSS"` | Uses CSS custom properties system |
|
|
426
|
-
|
|
427
|
-
---
|
|
428
|
-
|
|
429
|
-
## Data Attributes
|
|
430
|
-
|
|
431
|
-
The ThemeProvider automatically sets data attributes on `document.documentElement`:
|
|
432
|
-
|
|
433
|
-
```html
|
|
434
|
-
<!-- Light theme with TORCH mode -->
|
|
435
|
-
<html data-theme="light" data-theme-mode="TORCH">
|
|
436
|
-
|
|
437
|
-
<!-- Dark theme with CSS mode -->
|
|
438
|
-
<html data-theme="dark" data-theme-mode="CSS">
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
These attributes can be used in CSS selectors:
|
|
442
|
-
|
|
443
|
-
```css
|
|
444
|
-
[data-theme="light"] {
|
|
445
|
-
--background: white;
|
|
446
|
-
--foreground: black;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
[data-theme="dark"] {
|
|
450
|
-
--background: black;
|
|
451
|
-
--foreground: white;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
[data-theme-mode="TORCH"] {
|
|
455
|
-
/* TORCH-specific styles */
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
[data-theme-mode="CSS"] {
|
|
459
|
-
/* CSS-specific styles */
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
## LocalStorage Keys
|
|
466
|
-
|
|
467
|
-
The ThemeProvider stores values in localStorage:
|
|
468
|
-
|
|
469
|
-
| Key | Value | Description |
|
|
470
|
-
|-----|-------|-------------|
|
|
471
|
-
| `theme` | `"light" \| "dark" \| "default"` | Current theme preference |
|
|
472
|
-
| `theme-mode` | `"CSS" \| "TORCH"` | Current theme mode system |
|
|
473
|
-
|
|
474
|
-
---
|
|
475
|
-
|
|
476
|
-
## TypeScript Support
|
|
477
|
-
|
|
478
|
-
Full TypeScript support with type inference:
|
|
479
|
-
|
|
480
|
-
```typescript
|
|
481
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
482
|
-
|
|
483
|
-
function MyComponent() {
|
|
484
|
-
const { theme, themeMode, updateTheme, updateMode } = useTheme();
|
|
485
|
-
|
|
486
|
-
// Type: "light" | "dark" | "default"
|
|
487
|
-
const currentTheme: typeof theme = theme;
|
|
488
|
-
|
|
489
|
-
// Type: "CSS" | "TORCH"
|
|
490
|
-
const currentMode: typeof themeMode = themeMode;
|
|
491
|
-
|
|
492
|
-
// Type-safe updates
|
|
493
|
-
updateTheme('light'); // ✓
|
|
494
|
-
updateTheme('purple'); // ✗ TypeScript error
|
|
495
|
-
|
|
496
|
-
updateMode('CSS'); // ✓
|
|
497
|
-
updateMode('CUSTOM'); // ✗ TypeScript error
|
|
498
|
-
}
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
---
|
|
502
|
-
|
|
503
|
-
## Testing
|
|
504
|
-
|
|
505
|
-
### Testing Components with ThemeProvider
|
|
506
|
-
|
|
507
|
-
```tsx
|
|
508
|
-
import { render } from '@testing-library/react';
|
|
509
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
510
|
-
|
|
511
|
-
describe('ThemedComponent', () => {
|
|
512
|
-
it('should render with light theme', () => {
|
|
513
|
-
const { container } = render(
|
|
514
|
-
<ThemeProvider defaultTheme="light">
|
|
515
|
-
<ThemedComponent />
|
|
516
|
-
</ThemeProvider>
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
it('should render with dark theme', () => {
|
|
523
|
-
render(
|
|
524
|
-
<ThemeProvider defaultTheme="dark">
|
|
525
|
-
<ThemedComponent />
|
|
526
|
-
</ThemeProvider>
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
|
|
530
|
-
});
|
|
531
|
-
});
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
### Testing useTheme Hook
|
|
535
|
-
|
|
536
|
-
```tsx
|
|
537
|
-
import { renderHook, act } from '@testing-library/react';
|
|
538
|
-
import { ThemeProvider, useTheme } from "@/providers/ThemeProvider";
|
|
539
|
-
|
|
540
|
-
describe('useTheme', () => {
|
|
541
|
-
it('should update theme', () => {
|
|
542
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
543
|
-
<ThemeProvider defaultTheme="light">{children}</ThemeProvider>
|
|
544
|
-
);
|
|
545
|
-
|
|
546
|
-
const { result } = renderHook(() => useTheme(), { wrapper });
|
|
547
|
-
|
|
548
|
-
expect(result.current.theme).toBe('light');
|
|
549
|
-
|
|
550
|
-
act(() => {
|
|
551
|
-
result.current.updateTheme('dark');
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
expect(result.current.theme).toBe('dark');
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
it('should update theme mode', () => {
|
|
558
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
559
|
-
<ThemeProvider defaultThemeMode="TORCH">{children}</ThemeProvider>
|
|
560
|
-
);
|
|
561
|
-
|
|
562
|
-
const { result } = renderHook(() => useTheme(), { wrapper });
|
|
563
|
-
|
|
564
|
-
expect(result.current.themeMode).toBe('TORCH');
|
|
565
|
-
|
|
566
|
-
act(() => {
|
|
567
|
-
result.current.updateMode('CSS');
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
expect(result.current.themeMode).toBe('CSS');
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
it('should throw error when used outside provider', () => {
|
|
574
|
-
expect(() => {
|
|
575
|
-
renderHook(() => useTheme());
|
|
576
|
-
}).toThrow('useTheme must be used within a ThemeProvider');
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
### Mocking localStorage
|
|
582
|
-
|
|
583
|
-
```tsx
|
|
584
|
-
const localStorageMock = (() => {
|
|
585
|
-
let store: Record<string, string> = {};
|
|
586
|
-
|
|
587
|
-
return {
|
|
588
|
-
getItem: (key: string) => store[key] || null,
|
|
589
|
-
setItem: (key: string, value: string) => {
|
|
590
|
-
store[key] = value;
|
|
591
|
-
},
|
|
592
|
-
clear: () => {
|
|
593
|
-
store = {};
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
})();
|
|
597
|
-
|
|
598
|
-
Object.defineProperty(window, 'localStorage', {
|
|
599
|
-
value: localStorageMock
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
describe('ThemeProvider persistence', () => {
|
|
603
|
-
beforeEach(() => {
|
|
604
|
-
localStorageMock.clear();
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
it('should persist theme to localStorage', () => {
|
|
608
|
-
const { result } = renderHook(() => useTheme(), {
|
|
609
|
-
wrapper: ({ children }) => (
|
|
610
|
-
<ThemeProvider>{children}</ThemeProvider>
|
|
611
|
-
)
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
act(() => {
|
|
615
|
-
result.current.updateTheme('dark');
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
expect(localStorage.getItem('theme')).toBe('dark');
|
|
619
|
-
});
|
|
620
|
-
});
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
---
|
|
624
|
-
|
|
625
|
-
## Performance Considerations
|
|
626
|
-
|
|
627
|
-
### Preventing Re-renders
|
|
628
|
-
|
|
629
|
-
The ThemeProvider uses React context, which can cause re-renders. To optimize:
|
|
630
|
-
|
|
631
|
-
```tsx
|
|
632
|
-
import { memo } from 'react';
|
|
633
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
634
|
-
|
|
635
|
-
// Memoize components that don't need theme updates
|
|
636
|
-
const ExpensiveComponent = memo(() => {
|
|
637
|
-
return <div>Static content</div>;
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
// Only theme-dependent components re-render
|
|
641
|
-
function ThemedButton() {
|
|
642
|
-
const { theme } = useTheme();
|
|
643
|
-
return <button data-theme={theme}>Click me</button>;
|
|
644
|
-
}
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
### Selective Theme Consumption
|
|
648
|
-
|
|
649
|
-
```tsx
|
|
650
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
651
|
-
|
|
652
|
-
// Only consume what you need
|
|
653
|
-
function ThemeToggle() {
|
|
654
|
-
const { theme, updateTheme } = useTheme();
|
|
655
|
-
// Component only re-renders when theme changes
|
|
656
|
-
// Not affected by themeMode changes
|
|
657
|
-
|
|
658
|
-
return (
|
|
659
|
-
<button onClick={() => updateTheme(theme === 'light' ? 'dark' : 'light')}>
|
|
660
|
-
Toggle
|
|
661
|
-
</button>
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
---
|
|
667
|
-
|
|
668
|
-
## Best Practices
|
|
669
|
-
|
|
670
|
-
### 1. Wrap at Root Level
|
|
671
|
-
|
|
672
|
-
Always wrap your application at the highest level possible:
|
|
673
|
-
|
|
674
|
-
```tsx
|
|
675
|
-
// ✓ Good - Wrap entire app
|
|
676
|
-
function App() {
|
|
677
|
-
return (
|
|
678
|
-
<ThemeProvider>
|
|
679
|
-
<Router>
|
|
680
|
-
<Layout>
|
|
681
|
-
<Routes />
|
|
682
|
-
</Layout>
|
|
683
|
-
</Router>
|
|
684
|
-
</ThemeProvider>
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// ✗ Bad - Wrapping individual components
|
|
689
|
-
function Page() {
|
|
690
|
-
return (
|
|
691
|
-
<ThemeProvider>
|
|
692
|
-
<Component />
|
|
693
|
-
</ThemeProvider>
|
|
694
|
-
);
|
|
695
|
-
}
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
### 2. Use Data Attributes in CSS
|
|
699
|
-
|
|
700
|
-
Leverage the automatic data attributes for styling:
|
|
701
|
-
|
|
702
|
-
```css
|
|
703
|
-
/* Theme-specific styles */
|
|
704
|
-
[data-theme="light"] .card {
|
|
705
|
-
background: white;
|
|
706
|
-
color: black;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
[data-theme="dark"] .card {
|
|
710
|
-
background: #1a1a1a;
|
|
711
|
-
color: white;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/* Mode-specific styles */
|
|
715
|
-
[data-theme-mode="TORCH"] {
|
|
716
|
-
--primary: var(--torch-primary);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
[data-theme-mode="CSS"] {
|
|
720
|
-
--primary: var(--css-primary);
|
|
721
|
-
}
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
### 3. Handle SSR Properly
|
|
725
|
-
|
|
726
|
-
Avoid hydration mismatches:
|
|
727
|
-
|
|
728
|
-
```tsx
|
|
729
|
-
import { useEffect, useState } from 'react';
|
|
730
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
731
|
-
|
|
732
|
-
function ClientOnlyThemeButton() {
|
|
733
|
-
const [mounted, setMounted] = useState(false);
|
|
734
|
-
const { theme, updateTheme } = useTheme();
|
|
735
|
-
|
|
736
|
-
useEffect(() => {
|
|
737
|
-
setMounted(true);
|
|
738
|
-
}, []);
|
|
739
|
-
|
|
740
|
-
if (!mounted) {
|
|
741
|
-
return <button>Theme</button>; // Placeholder during SSR
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
return (
|
|
745
|
-
<button onClick={() => updateTheme(theme === 'light' ? 'dark' : 'light')}>
|
|
746
|
-
{theme === 'light' ? '🌙' : '☀️'}
|
|
747
|
-
</button>
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
### 4. Provide User Feedback
|
|
753
|
-
|
|
754
|
-
Show visual feedback when theme changes:
|
|
755
|
-
|
|
756
|
-
```tsx
|
|
757
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
758
|
-
import { useState, useEffect } from 'react';
|
|
759
|
-
|
|
760
|
-
function ThemeToggleWithFeedback() {
|
|
761
|
-
const { theme, updateTheme } = useTheme();
|
|
762
|
-
const [isChanging, setIsChanging] = useState(false);
|
|
763
|
-
|
|
764
|
-
const handleToggle = () => {
|
|
765
|
-
setIsChanging(true);
|
|
766
|
-
updateTheme(theme === 'light' ? 'dark' : 'light');
|
|
767
|
-
setTimeout(() => setIsChanging(false), 300);
|
|
768
|
-
};
|
|
769
|
-
|
|
770
|
-
return (
|
|
771
|
-
<button
|
|
772
|
-
onClick={handleToggle}
|
|
773
|
-
className={cn('theme-toggle', isChanging && 'transitioning')}
|
|
774
|
-
>
|
|
775
|
-
{theme === 'light' ? '🌙' : '☀️'}
|
|
776
|
-
</button>
|
|
777
|
-
);
|
|
778
|
-
}
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
### 5. Validate Theme Values
|
|
782
|
-
|
|
783
|
-
Ensure theme values are valid before updating:
|
|
784
|
-
|
|
785
|
-
```tsx
|
|
786
|
-
import { useTheme } from "@/providers/ThemeProvider";
|
|
787
|
-
|
|
788
|
-
function SafeThemeUpdater({ theme }: { theme: string }) {
|
|
789
|
-
const { updateTheme } = useTheme();
|
|
790
|
-
|
|
791
|
-
const handleUpdate = () => {
|
|
792
|
-
if (theme === 'light' || theme === 'dark' || theme === 'default') {
|
|
793
|
-
updateTheme(theme as any);
|
|
794
|
-
} else {
|
|
795
|
-
console.error('Invalid theme value:', theme);
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
|
|
799
|
-
return <button onClick={handleUpdate}>Set Theme</button>;
|
|
800
|
-
}
|
|
801
|
-
```
|
|
802
|
-
|
|
803
|
-
---
|
|
804
|
-
|
|
805
|
-
## Accessibility
|
|
806
|
-
|
|
807
|
-
The ThemeProvider supports accessibility best practices:
|
|
808
|
-
|
|
809
|
-
1. **Respects System Preferences**: The `"default"` theme follows `prefers-color-scheme`
|
|
810
|
-
2. **Persistent Preferences**: User choices are saved to localStorage
|
|
811
|
-
3. **ARIA Labels**: Add descriptive labels to theme toggles
|
|
812
|
-
|
|
813
|
-
```tsx
|
|
814
|
-
<button
|
|
815
|
-
onClick={() => updateTheme(theme === 'light' ? 'dark' : 'light')}
|
|
816
|
-
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} theme`}
|
|
817
|
-
aria-pressed={theme === 'dark'}
|
|
818
|
-
>
|
|
819
|
-
{theme === 'light' ? '🌙 Dark Mode' : '☀️ Light Mode'}
|
|
820
|
-
</button>
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
---
|
|
824
|
-
|
|
825
|
-
## Related Components
|
|
826
|
-
|
|
827
|
-
All components in the library can consume the theme context through the `theme` prop or by using `data-theme` attributes in CSS.
|
|
828
|
-
|
|
829
|
-
---
|
|
830
|
-
|
|
831
|
-
## Browser Support
|
|
832
|
-
|
|
833
|
-
- Chrome 90+
|
|
834
|
-
- Firefox 88+
|
|
835
|
-
- Safari 14+
|
|
836
|
-
- Edge 90+
|
|
837
|
-
|
|
838
|
-
**LocalStorage**: Supported in all modern browsers. Gracefully degrades if unavailable.
|
|
839
|
-
|
|
840
|
-
---
|
|
841
|
-
|
|
842
|
-
## Migration Guide
|
|
843
|
-
|
|
844
|
-
### From v1.x to v2.x
|
|
845
|
-
|
|
846
|
-
```tsx
|
|
847
|
-
// v1.x (old)
|
|
848
|
-
import { Theme } from 'old-library';
|
|
849
|
-
<Theme mode="dark">
|
|
850
|
-
|
|
851
|
-
// v2.x (new)
|
|
852
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
853
|
-
<ThemeProvider defaultTheme="dark">
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
### From Other Theme Providers
|
|
857
|
-
|
|
858
|
-
```tsx
|
|
859
|
-
// next-themes
|
|
860
|
-
import { ThemeProvider as NextThemeProvider } from 'next-themes';
|
|
861
|
-
|
|
862
|
-
// TORCH Glare
|
|
863
|
-
import { ThemeProvider } from "@/providers/ThemeProvider";
|
|
864
|
-
|
|
865
|
-
// Both can coexist if needed, but use TORCH Glare for consistency
|
|
866
|
-
```
|