Skip to content

Commit 90aa4b4

Browse files
authored
feat(dialtone-css, dialtone-documentation): DLT-3363 add theme-to-mode migration guide and codemod (#1260)
1 parent 046f212 commit 90aa4b4

7 files changed

Lines changed: 559 additions & 9 deletions

File tree

apps/dialtone-documentation/docs/_data/site-nav.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@
287287
"text": "Scrollbar :never → :always",
288288
"link": "/guides/migration/scrollbar-always/"
289289
},
290+
{
291+
"text": "Theme to Mode",
292+
"link": "/guides/migration/theme-to-mode/"
293+
},
290294
{
291295
"text": "DtModal Native Dialog",
292296
"link": "/guides/migration/modal-native-dialog/"

apps/dialtone-documentation/docs/dialtone/whats-new/posts/2024-8-1.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ excerpt: 'Dialtone now supports multiple themes including T-Mobile and Marketing
77

88
<BlogPost :author="$frontmatter.author" :posted="parse($frontmatter.posted, 'y-M-d', new Date())" :heading="$frontmatter.heading" :excerpt="$frontmatter.excerpt">
99

10+
> [!WARNING] This post documents the legacy theme API
11+
> The `setTheme` API and theme JSON imports shown below have been superseded by the layered API on `next`. See the [Theme to Mode migration guide](/guides/migration/theme-to-mode/) for current usage.
12+
1013
Hi everyone! We're happy to announce the release of Dialtone theming. You will now be able to set themes beyond just light mode and dark mode. For example you can now apply the design token based T-Mobile theme in both light mode and dark mode as well as the standard Dialpad theme. Semantic tokens will all have to be used correctly in your application for this to work.
1114

1215
This release will have a small breaking change involving the way you initialize Dialtone even if you don't intend to use themes. Please update your projects as mentioned below. The readme on GitHub has also been updated with the new steps.

apps/dialtone-documentation/docs/guides/migration/index.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ Work through each applicable guide in order. Guides earlier in the list are prer
4242

4343
| # | Guide | Breaking? | Tool | Summary |
4444
| --- | --- | --- | --- | --- |
45-
| 15 | [Vue 2 Removal](./vue2-removal/) | **Yes** || Vue 2 support dropped. Last Vue 2 version: `9.154.0`. |
45+
| 17 | [Theme to Mode](./theme-to-mode/) | **Yes** | `dialtone-migration-helper` | Legacy `setTheme` deprecated. New layered API uses `setMode` / `setBrand` / `setContrast` / `initDialtoneTheme`. Root attributes `data-dt-theme``data-dt-mode` + `data-dt-brand` + `data-dt-contrast`. |
46+
| 18 | [Vue 2 Removal](./vue2-removal/) | **Yes** || Vue 2 support dropped. Last Vue 2 version: `9.154.0`. |
4647

4748
## Quick Start
4849

@@ -73,29 +74,33 @@ npx dialtone-migration-helper --cwd ./src
7374
npx dialtone-migration-helper --cwd ./src
7475
# Select "success-to-positive"
7576

76-
# 7. Flex to Stack
77+
# 7. Theme to Mode (deprecates setTheme, switches to layered API)
78+
npx dialtone-migration-helper --cwd ./src
79+
# Select "theme to mode"
80+
81+
# 8. Flex to Stack
7782
npx dialtone-migrate-flex-to-stack --cwd ./src
7883

79-
# 8. Link and Button navigation (anchor/router-link to DtButton/DtLink)
84+
# 9. Link and Button navigation (anchor/router-link to DtButton/DtLink)
8085
npx dialtone-migrate-link-rendering --cwd ./src
8186

82-
# 9. T-shirt sizes to numeric
87+
# 10. T-shirt sizes to numeric
8388
npx dialtone-migrate-tshirt-to-numeric --cwd ./src
8489

85-
# 10. Physical to logical naming
90+
# 11. Physical to logical naming
8691
npx dialtone-migration-helper --cwd ./src
8792
# Select "physical-to-logical"
8893

89-
# 11. Component props, events, and slots
94+
# 12. Component props, events, and slots
9095
npx dialtone-migrate-props --cwd ./src
9196

92-
# 12. DtChip interactive default (adds :interactive="true" to clickable chips)
97+
# 13. DtChip interactive default (adds :interactive="true" to clickable chips)
9398
npx dialtone-migrate-chip-interactive --cwd ./src
9499

95-
# 13. Scrollbar :never → :always
100+
# 14. Scrollbar :never → :always
96101
npx dialtone-migrate-scrollbar-always --cwd ./src
97102

98-
# 14. ESLint auto-fix pass
103+
# 15. ESLint auto-fix pass
99104
npx eslint --fix "src/**/*.vue"
100105
```
101106

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
---
2+
title: "Theme: setTheme deprecated, layered API replaces it"
3+
description: "The legacy setTheme() function and data-dt-theme attribute have been deprecated in favor of the new layered API: initDialtoneTheme, setMode, setBrand, setContrast, and setMaterial."
4+
---
5+
6+
## TLDR
7+
8+
> [!WARNING] Breaking change
9+
> `setTheme()` is deprecated and `data-dt-theme` no longer set by default. Projects adopting `next` must migrate to the layered API. Run the migration script to automate the transition.
10+
11+
- `setTheme()` still works but is deprecated. Migrate to `initDialtoneTheme()` for startup, `setMode()` / `setBrand()` / `setContrast()` / `setMaterial()` for runtime switching.
12+
- Root attributes: `data-dt-theme` is no longer set. New attributes are `data-dt-mode`, `data-dt-brand`, `data-dt-contrast`, and `data-dt-material`.
13+
- Run `npx dialtone-migration-helper` and select **theme to mode** to automate most of this.
14+
15+
## Why
16+
17+
The old `setTheme()` model coupled three independent concerns — color mode (light/dark), brand (dp/tmo/melon), and contrast (default/high) — into a single monolithic theme object. Switching from light to dark required swapping the entire object, including brand tokens that hadn't changed.
18+
19+
The layered API separates these four orthogonal dimensions:
20+
21+
- **Mode** (`setMode`) — light or dark. Changes instantly with a single attribute toggle.
22+
- **Brand** (`setBrand`) — which color palette overrides to apply (dp, tmo, melon, etc.).
23+
- **Contrast** (`setContrast`) — default or high contrast, for WCAG AAA accessibility.
24+
- **Material** (`setMaterial`) — surface texture layer (`sandstone` default, plus `steel`, `graphite`, `iron`, `amethyst`, `jade`). Applied independently of mode or brand; some brands lock their material — see [Brand-locked materials](/guides/theme-and-mode/#brand-locked-materials) for details.
25+
26+
You can now switch any dimension independently, which reduces bundle work, enables runtime contrast toggles without a full theme reload, and maps cleanly to user preferences (OS dark-mode + a brand choice + an accessibility setting + a material preference are four separate controls).
27+
28+
## What Changed
29+
30+
| | Before | After |
31+
| --- | --- | --- |
32+
| Startup call | `setTheme(DpLight)` | `initDialtoneTheme(Dp, 'light')` |
33+
| Mode switching | `setTheme(DpDark)` | `setMode('dark')` |
34+
| Brand switching | `setTheme(TmoLight)` | `setBrand(Tmo)` |
35+
| Contrast | `setTheme(theme, root, HighContrast)` | `setContrast(HighContrast)` |
36+
| Disable contrast | `setTheme(theme, root, null)` | `setContrast(null)` |
37+
| Material | *(not available)* | `setMaterial('sandstone')` |
38+
| Root attribute (mode) | `data-dt-theme="dp-light"` | `data-dt-mode="light"` |
39+
| Root attribute (brand) | `data-dt-brand="dp"` *(already existed)* | `data-dt-brand="dp"` *(unchanged)* |
40+
| Root attribute (contrast) | `data-dt-contrast="default"` *(already existed)* | `data-dt-contrast="default"` *(unchanged)* |
41+
| Root attribute (material) | *(not available)* | `data-dt-material="sandstone"` |
42+
| CSS selector | `[data-dt-theme="dp-light"]` | `[data-dt-mode="light"]` |
43+
44+
> [!INFO] setTheme still works
45+
> `setTheme()` is not removed. It continues to work with both legacy theme objects and the new layered format. Deprecation means it will be removed in a future major release. The migration script handles the rewrite automatically.
46+
47+
## Quick Checklist
48+
49+
1. Run the migration script: `npx dialtone-migration-helper --cwd ./src` and select **theme to mode**.
50+
2. Review any `data-dt-theme="invert"` patterns the script flagged — decide whether each should adopt `v-dt-mode` (see [Manual Review](#manual-review-for-v-dt-mode-candidates)).
51+
3. Smoke-test your app: toggle light/dark, switch brand if applicable, toggle high contrast if used, apply a material if your app uses one.
52+
53+
## Migration
54+
55+
Run the migration helper from your project root:
56+
57+
```bash
58+
npx dialtone-migration-helper --cwd ./src
59+
```
60+
61+
Select **theme to mode** from the interactive menu. Add `--dry-run` to preview changes without writing files. Add `--yes` to apply without prompting.
62+
63+
The script handles the patterns below automatically. After it runs, also run `npx eslint --fix` to clean up the `setTheme` named import that becomes unused after the call-site rewrite.
64+
65+
### Startup call
66+
67+
<div class="d-d-grid d-g-200 d-g-cols1 md:d-g-cols2">
68+
<div>
69+
70+
### Before
71+
72+
```js
73+
setTheme(DpLight);
74+
```
75+
76+
</div>
77+
<div>
78+
79+
### After
80+
81+
```js
82+
initDialtoneTheme(Dp, 'light');
83+
```
84+
85+
</div>
86+
</div>
87+
88+
Call `initDialtoneTheme()` once on startup. It loads core tokens, sets the initial mode and brand, and sets contrast to `'default'`.
89+
90+
### Runtime mode switching
91+
92+
<div class="d-d-grid d-g-200 d-g-cols1 md:d-g-cols2">
93+
<div>
94+
95+
### Before
96+
97+
```js
98+
setTheme(DpDark);
99+
```
100+
101+
</div>
102+
<div>
103+
104+
### After
105+
106+
```js
107+
setMode('dark');
108+
```
109+
110+
</div>
111+
</div>
112+
113+
### Root attribute (HTML/CSS)
114+
115+
<div class="d-d-grid d-g-200 d-g-cols1 md:d-g-cols2">
116+
<div>
117+
118+
### Before
119+
120+
```html
121+
<html data-dt-theme="dp-light">
122+
```
123+
124+
```css
125+
[data-dt-theme="dp-light"] .d-banner { ... }
126+
```
127+
128+
</div>
129+
<div>
130+
131+
### After
132+
133+
```html
134+
<html data-dt-mode="light" data-dt-brand="dp" data-dt-contrast="default" data-dt-material="sandstone">
135+
```
136+
137+
```css
138+
[data-dt-mode="light"] .d-banner { ... }
139+
```
140+
141+
</div>
142+
</div>
143+
144+
If your code reads `getAttribute('data-dt-theme')` or sets it manually, the migration script rewrites `setAttribute`/`getAttribute` call first arguments and CSS `[data-dt-theme]` selectors automatically.
145+
146+
### Contrast
147+
148+
<div class="d-d-grid d-g-200 d-g-cols1 md:d-g-cols2">
149+
<div>
150+
151+
### Before
152+
153+
```js
154+
import HighContrast from '@dialpad/dialtone/themes/high-contrast';
155+
156+
// Enable on init
157+
setTheme(DpLight, document.documentElement, HighContrast);
158+
159+
// Toggle on/off — required full re-init
160+
setTheme(DpLight);
161+
setTheme(DpLight, document.documentElement, HighContrast);
162+
```
163+
164+
</div>
165+
<div>
166+
167+
### After
168+
169+
```js
170+
import HighContrast from '@dialpad/dialtone/themes/high-contrast';
171+
172+
// Enable
173+
setContrast(HighContrast);
174+
175+
// Disable (return to default)
176+
setContrast(null);
177+
```
178+
179+
</div>
180+
</div>
181+
182+
## Manual Review for v-dt-mode Candidates
183+
184+
The script flags `data-dt-theme="invert"` patterns with a comment rather than auto-rewriting them:
185+
186+
```html
187+
<!-- TODO: review for v-dt-mode adoption — see /guides/migration/theme-to-mode/ -->
188+
<section data-dt-theme="invert">...</section>
189+
```
190+
191+
**Why not auto-rewrite?** The `v-dt-mode` directive resolves the inverted mode against the *live* parent mode using a MutationObserver — it stays reactive when the parent mode changes at runtime. A static `data-dt-mode="dark"` replacement would be wrong for any component sitting inside a dynamic parent. The correct rewrite depends on what the surrounding mode context is in your app.
192+
193+
For each flagged location, decide:
194+
195+
**Adopt `v-dt-mode` (recommended when the parent mode is dynamic):**
196+
197+
```html
198+
<!-- Before: manually inverted region -->
199+
<section data-dt-theme="invert">Dark island inside a light page</section>
200+
201+
<!-- After: reactive invert via directive -->
202+
<section v-dt-mode>Dark island inside a light page</section>
203+
```
204+
205+
```js
206+
import { DtModeDirective } from '@dialpad/dialtone/vue';
207+
app.use(DtModeDirective);
208+
```
209+
210+
**Keep a static override (acceptable when the parent mode never changes):**
211+
212+
```html
213+
<section data-dt-mode="dark">Always dark regardless of parent</section>
214+
```
215+
216+
**Use `DtModeIsland` (when you need a styled container with background):**
217+
218+
```html
219+
<dt-mode-island mode="invert">Inverted region with surface color</dt-mode-island>
220+
```
221+
222+
For full directive documentation see the [v-dt-mode Storybook page](https://dialtone.dialpad.com/vue/next/?path=/docs/directives-mode--docs). For `DtModeIsland`, see the [Mode Island component page](/components/mode-island.html).
223+
224+
> [!INFO] Dynamic bindings and template literals
225+
> The script only rewrites static `data-dt-theme="invert"` string literals. Vue dynamic bindings (`:data-dt-theme="expr"`) and JavaScript template literals are not covered. After running the script, grep your codebase for any remaining `data-dt-theme` references: `grep -r 'data-dt-theme' ./src`.
226+
227+
## Need Help?
228+
229+
If you have any troubles, please let us know in the **#dialtone** Dialpad channel.

0 commit comments

Comments
 (0)