Skip to content

Commit 5c72c6c

Browse files
francisrupertbraddialpadclaude
authored
feat(tokens): DLT-2802 DLT-2563 layered theming system and mode island component (#945)
Co-authored-by: Brad Paugh <brad.paugh@dialpad.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8089e8c commit 5c72c6c

57 files changed

Lines changed: 5495 additions & 799 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 111 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,92 +22,145 @@ npm install @dialpad/dialtone @dialpad/i18n
2222

2323
```shell
2424
npm install @dialpad/dialtone @dialpad/i18n-vue2
25+
```
2526

26-
### Import packages:
27+
---
2728

28-
#### Without theming
29+
### Theming
2930

30-
If you don't care about theming and just want to use Dialtone with the default light theme:
31+
##### Quick Start
3132

32-
- CSS
33+
**Install packages:**
3334

34-
```css
35-
@import "@dialpad/dialtone/css-default-theme";
35+
```shell
36+
npm install @dialpad/dialtone @dialpad/dialtone-tokens
3637
```
3738

38-
- Javascript
39+
**Initialize (main.js or App.vue):**
3940

4041
```js
41-
import "@dialpad/dialtone/css-default-theme";
42+
import { initDialtoneTheme } from '@dialpad/dialtone/themes/config';
43+
import Dp from '@dialpad/dialtone-tokens/themes/dp';
44+
45+
initDialtoneTheme(Dp, 'light');
4246
```
4347

44-
#### With theming
48+
Done. Your app now has theming.
4549

46-
If you want to use theming, import from the below path. This file does not include design tokens so it is required to also set a theme to apply design tokens to the root element.
50+
---
4751

48-
- CSS
52+
##### Basic Usage
4953

50-
```css
51-
@import "@dialpad/dialtone/css";
54+
**Toggle light/dark mode:**
55+
56+
```js
57+
import { setMode } from '@dialpad/dialtone/themes/config';
58+
setMode('dark');
5259
```
5360

54-
- Javascript
61+
**Use different theme at startup:**
5562

5663
```js
57-
import "@dialpad/dialtone/css";
64+
import Melon from '@dialpad/dialtone-tokens/themes/melon';
65+
initDialtoneTheme(Melon, 'light');
5866
```
5967

60-
##### Set theme via setTheme() javascript function (preferred)
68+
**Switch themes dynamically:**
69+
70+
```js
71+
import { setBrand } from '@dialpad/dialtone/themes/config';
72+
import Tmo from '@dialpad/dialtone-tokens/themes/tmo';
73+
setBrand(Tmo);
74+
```
6175

62-
Import the theme you want to use and set it via the `setTheme` function:
76+
**Enable high contrast:**
6377

6478
```js
65-
import { setTheme } from '@dialpad/dialtone/themes/config';
66-
import DpLight from '@dialpad/dialtone/themes/dp-light';
67-
setTheme(DpLight);
79+
import { setContrast } from '@dialpad/dialtone/themes/config';
80+
import HighContrast from '@dialpad/dialtone-tokens/themes/high-contrast';
81+
82+
setContrast(HighContrast);
83+
setContrast(null); // disable
6884
```
6985

70-
Possible themes are as follows:
86+
---
87+
88+
##### Available Themes
89+
90+
51 themes total. Use any with `initDialtoneTheme()` or `setBrand()`.
91+
92+
**Standard:** dp, tmo, aegean, botany, buttercream, high-desert, melon, plum, sunflower, verdant-haze
93+
94+
**Accessibility:** prota-deuter, trita
7195

72-
- DpLight - Dialpad Light
73-
- DpDark - Dialpad Dark
74-
- TmoLight - T-Mobile Light
75-
- TmoDark - T-Mobile Dark
76-
- ExpressiveLight - Marketing Light
77-
- ExpressiveDark - Marketing Dark
78-
- ExpressiveSmLight - Marketing Small Light
79-
- ExpressiveSmDark - Marketing Small Dark
96+
**Experimental:** 101 through 137
8097

81-
There is an optional second parameter to `setTheme` that allows you to set the theme on a specific element. This is useful in the case of a Shadow DOM
82-
when you want to apply the theme to the root element of the shadow DOM rather than the document root. If you do not set this parameter the theme will be applied to the document root.
98+
**Contrast:** high-contrast
99+
100+
**Import pattern:**
83101

84102
```js
85-
import { setTheme } from '@dialpad/dialtone/themes/config';
86-
import DpLight from '@dialpad/dialtone/themes/dp-light';
87-
setTheme(DpLight, document.querySelector('#my-shadow-root-host'));
103+
import ThemeName from '@dialpad/dialtone-tokens/themes/theme-name';
88104
```
89105

90-
##### Set theme manually by importing files
106+
---
107+
108+
##### Advanced
91109

92-
You may want to use this method if you are unable to use javascript.
110+
**Shadow DOM (Web Components):**
93111

94-
You need to import two tokens files in order to apply a theme. A base tokens files, which is either light or dark, and
95-
a semantic brand tokens file which is named after a brand and theme 'tokens-dp-light', 'tokens-dp-dark', 'tokens-tmo-light', ...
112+
Pass host element as third parameter.
96113

97-
- CSS
114+
```js
115+
initDialtoneTheme(Dp, 'light', this);
116+
```
117+
118+
**CSS only (no JS):**
98119

99120
```css
100-
@import "@dialpad/dialtone/tokens/tokens-base-light.css" // Base light theme
101-
@import "@dialpad/dialtone/tokens/tokens-dp-light.css" // Dialpad light brand
121+
@import "@dialpad/dialtone-tokens/layered/tokens-core.css";
122+
@import "@dialpad/dialtone-tokens/layered/tokens-base-colors.css";
123+
@import "@dialpad/dialtone-tokens/layered/tokens-dp-colors.css";
124+
```
125+
126+
Then set attributes:
127+
128+
```html
129+
<html data-dt-mode="light" data-dt-brand="dp" data-dt-contrast="default">
102130
```
103131

104-
- Javascript
132+
**Mode sections:**
133+
134+
See [Mode Island component](https://dialtone.dialpad.com/components/mode-island.html) docs.
135+
136+
---
137+
138+
##### Legacy Theming System (Backward Compatible)
139+
140+
The original theming system remains fully supported for existing projects. New projects should use the layered system above for better performance and smaller bundle sizes.
141+
142+
**Note:** Both systems support Shadow DOM identically - pass the host element as the second parameter.
105143

106144
```js
107-
import "@dialpad/dialtone/tokens/tokens-base-light.css" // Base light theme
108-
import "@dialpad/dialtone/tokens/tokens-dp-light.css" // Dialpad light brand
145+
import { setTheme } from '@dialpad/dialtone/themes/config';
146+
import DpLight from '@dialpad/dialtone/themes/dp-light';
147+
import DpDark from '@dialpad/dialtone/themes/dp-dark';
148+
import TmoLight from '@dialpad/dialtone/themes/tmo-light';
149+
import TmoDark from '@dialpad/dialtone/themes/tmo-dark';
150+
151+
// Set theme (automatically detected as legacy)
152+
setTheme(DpLight);
153+
154+
// Shadow DOM support
155+
setTheme(DpLight, document.querySelector('#my-shadow-root-host'));
109156
```
110157

158+
**Legacy themes available:**
159+
160+
- `DpLight`, `DpDark`, `TmoLight`, `TmoDark`
161+
162+
**Note:** Legacy system loads complete token files (~1256KB per theme). Consider migrating to layered system for better performance.
163+
111164
#### Dialtone icons
112165

113166
- Vue 2:
@@ -193,36 +246,37 @@ the library.
193246

194247
#### How does our bundling works
195248

196-
To achieve this we needed to create certain configs through the monorepo to be able to handle them even if
249+
To achieve this we needed to create certain configs through the monorepo to be able to handle them even if
197250
they have the same package name e.g: `@dialpad/dialtone-vue`.
198251

199252
1. In root [package.json](package.json):
200-
- `pnpm`:
201-
- `peerDependencyRules` include `vue": "^2.6 || ^3.2"` to make sure we don't have warnings related to vue version
253+
- `pnpm`:
254+
- `peerDependencyRules` include `vue": "^2.6 || ^3.2"` to make sure we don't have warnings related to vue version
202255
mismatch.
203256
- `packageExtensions` tells pnpm which Vue version to use for each package.
204-
- `dependencies` doesn't include any specific Vue 2 or Vue 3 dependencies as this causes issues on the client when
257+
- `dependencies` doesn't include any specific Vue 2 or Vue 3 dependencies as this causes issues on the client when
205258
trying to use exports from `./vue2` or `./vue3`.
206259
2. On individual packages `package.json` files:
207260
- Include the specific dependencies in case someone uses the individual package
208-
- In `vite.config.js`[Vue 2](packages/dialtone-vue2/vite.config.js),
209-
[Vue 3](packages/dialtone-vue3/vite.config.js) add dependencies to external to make sure they don't cause
210-
issues on product. (This is more specific for the Vue 2 package, as product is depending on Vue 2.6 and any
261+
- In `vite.config.js`[Vue 2](packages/dialtone-vue2/vite.config.js),
262+
[Vue 3](packages/dialtone-vue3/vite.config.js) add dependencies to external to make sure they don't cause
263+
issues on product. (This is more specific for the Vue 2 package, as product is depending on Vue 2.6 and any
211264
dependency that needs a newer Vue version will cause issues).
212265
3. In [project.json](project.json)
213266
- Include implicit dependencies to make sure NX builds them before trying to copy the files to the mono-package.
214267
4. In `gulpfile.cjs`
215268
- Copy the built files into the root `dist` folder.
216269

217270
#### Included packages
271+
218272
- Dialtone CSS
219273
- Dialtone Tokens
220274
- Dialtone Vue 2
221275
- Dialtone Vue 3
222276

223277
### Tree-shaking
224278

225-
Tree-shaking is a feature that allows you to remove unused code from your bundle, and it is enabled by default in our
279+
Tree-shaking is a feature that allows you to remove unused code from your bundle, and it is enabled by default in our
226280
build process for Dialtone, Dialtone Vue, Dialtone Combinator and Dialtone Icons.
227281

228282
We achieve tree-shaking primarily via three mechanisms across the packages:
@@ -240,14 +294,15 @@ We achieve tree-shaking primarily via three mechanisms across the packages:
240294
#### Publishing ESM builds (with dual ESM/CJS via exports map)
241295

242296
Packages expose ESM for bundlers to statically analyze and tree-shake, with CJS fallbacks.
243-
- `@dialpad/dialtone-vue` (vue3):
297+
298+
- `@dialpad/dialtone-vue` (vue3):
244299
- `"type"`: `"module"`,
245300
- `"module"`: `"./dist/dialtone-vue.js"`,
246301
- `"main"`: `"./dist/dialtone-vue.cjs"`,
247302

248303
#### Deep, per-module entry points to enable fine-grained import paths
249304

250-
Exports maps expose subpath entries so consumers can import only what they need (which aids tree-shaking and avoids
305+
Exports maps expose subpath entries so consumers can import only what they need (which aids tree-shaking and avoids
251306
pulling entire bundles):
252307

253308
- `@dialpad/dialtone` exposes `./vue3/lib/*` and `./vue2/lib/*` map to individual component imports.
@@ -546,3 +601,5 @@ These will generate a JSON and HTML report in the `coverage` directory.
546601

547602
The coverage thresholds are defined in the `vitest.config.ts` file.
548603
When submitting a PR the CI will run the tests with coverage and fail if the coverage is below the thresholds.
604+
605+
<!-- test -->

apps/dialtone-documentation/docs/.vuepress/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export default defineUserConfig({
9696
'@views': path.resolve(__dirname, './views'),
9797
'@mixins': path.resolve(__dirname, './common/mixins/'),
9898
'@utilities': path.resolve(__dirname, './common/utilities.js'),
99+
'@composables': path.resolve(__dirname, './theme/composables'),
99100
'@projectRoot': path.resolve(__dirname, '../../'),
100101
'@': path.resolve(__dirname, '../'),
101102
'@workspaceRoot': path.resolve(__dirname, '../../../../'),

apps/dialtone-documentation/docs/.vuepress/theme/assets/less/dialtone-docs.less

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -297,20 +297,21 @@ html body {
297297
// border-inline-end: var(--dt-size-100) solid var(--dt-shell-color-border-subtle);
298298

299299
&__list {
300-
padding: var(--dt-size-550) var(--dt-size-450) var(--dt-size-750);
300+
padding: var(--dt-size-550) 0 var(--dt-size-750) var(--dt-size-450);
301301
inset-block: var(--dt-space-700) 0;
302302
inline-size: calc(var(--dt-size-300) * 64);
303303
}
304304
}
305305

306306
.dialtone-content {
307307
background-color: var(--dt-color-surface-primary);
308-
border: var(--dt-size-border-100) solid var(--dt-color-border-subtle);
309-
border-radius: var(--dt-size-radius-500);
308+
border-block: var(--dt-size-border-100) solid var(--dt-color-border-subtle);
310309

311310
@media (min-width: 980px) {
312-
margin-right: var(--dt-space-500);
313311
margin-bottom: var(--dt-space-500);
312+
border: var(--dt-size-border-100) solid var(--dt-color-border-subtle);
313+
border-radius: var(--dt-size-radius-500);
314+
margin-inline: var(--dt-space-500);
314315
}
315316
}
316317

@@ -678,26 +679,28 @@ a.header-anchor {
678679

679680
.dialtone-wall {
680681
display: grid;
681-
grid-gap: var(--dt-space-500);
682+
gap: var(--dt-space-500);
682683
margin-block-start: var(--dt-space-500);
683684

684685
@media screen and (min-width: 640px) {
685-
grid-gap: var(--dt-space-600);
686686
grid-template-columns: [full-start] repeat(2, [col-start] var(--col-width, minmax(0, 1fr)) [col-end]) [full-end] !important;
687687
}
688688

689689
@media screen and (min-width: 768px) {
690690
grid-template-columns: [full-start] repeat(3, [col-start] var(--col-width, minmax(0, 1fr)) [col-end]) [full-end] !important;
691691
}
692692

693+
@media screen and (min-width: 1354px) {
694+
gap: var(--dt-space-600);
695+
}
696+
693697
&__item {
694698
display: block;
695699
overflow: hidden;
696700
text-decoration: none;
697-
background-color: var(--dt-color-surface-primary);
698-
border: var(--dt-size-100) solid;
699-
border-color: var(--dt-color-border-default);
700-
border-radius: var(--dt-size-400);
701+
background-color: var(--dt-color-surface-secondary);
702+
border: var(--dt-size-border-100) solid var(--dt-color-border-subtle);
703+
border-radius: var(--dt-size-radius-400);
701704
opacity: .7;
702705
transition-timing-function: var(--ttf-in-out);
703706
transition-duration: var(--td200);
@@ -709,7 +712,7 @@ a.header-anchor {
709712

710713
&:is(a):hover {
711714
text-decoration: none;
712-
border-color: var(--dt-color-border-moderate);
715+
border-color: var(--dt-color-border-default);
713716
box-shadow: var(--dt-shadow-small);
714717
}
715718

@@ -756,9 +759,14 @@ a.header-anchor {
756759
}
757760

758761
&__description {
759-
color: var(--dt-color-foreground-tertiary);
760-
font-size: var(--dt-font-size-200);
761-
line-height: var(--dt-font-line-height-300);
762+
display: none;
763+
764+
@media screen and (min-width: 768px) {
765+
display: block;
766+
color: var(--dt-color-foreground-tertiary);
767+
font-size: var(--dt-font-size-200);
768+
line-height: var(--dt-font-line-height-300);
769+
}
762770
}
763771
}
764772

0 commit comments

Comments
 (0)