Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .nx/version-plans/version-plan-1768323163192.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
'@ledgerhq/lumen-design-core': patch
'@ledgerhq/lumen-ui-react': patch
---

BREAKING_CHANGE(ui-react): Upgrade Tailwind to v4

## Migration Guide

### 1. Upgrade Tailwind CSS

Run the official upgrade tool to automatically migrate your project:

```bash
npx @tailwindcss/upgrade
```

This will update your dependencies, migrate your CSS configuration, and update any deprecated utility classes.

For detailed migration steps, see the official [Tailwind CSS v4 Upgrade Guide](https://tailwindcss.com/docs/upgrade-guide).

### 2. Install PostCSS Plugin

In Tailwind v4, the PostCSS plugin no longer lives in the `tailwindcss` package. Install the dedicated package:

```bash
npm install -D @tailwindcss/postcss
```

#### Using Vite

If you're using Vite, we recommend migrating from the PostCSS plugin to the dedicated Vite plugin for improved performance and the best developer experience:

```bash
npm install -D @tailwindcss/vite
```

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
plugins: [tailwindcss()],
});
```

### 3. Update ESLint Plugin

Replace `eslint-plugin-tailwindcss` with [eslint-plugin-better-tailwindcss](https://github.com/schoero/eslint-plugin-better-tailwindcss).

> **Why?** The original `eslint-plugin-tailwindcss` does not support Tailwind v4 and has been inactive for a while ([see issue](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/325)).

```bash
npm install -D eslint-plugin-better-tailwindcss
```

Configure the `entryPoint` in your ESLint config to point to your main Tailwind stylesheet (e.g., `./src/styles.css`). See our [eslint.config.mjs](https://github.com/LedgerHQ/lumen-design-system/blob/main/libs/ui-react/eslint.config.mjs) for a complete configuration example.

> **Note:** In NX monorepos, you might need the beta version due to an issue with `entryPoint` not being resolved correctly ([see issue](https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/154)):
>
> ```bash
> npm install -D eslint-plugin-better-tailwindcss@beta
> ```
5 changes: 1 addition & 4 deletions apps/app-sandbox-react/postcss.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

export default {
plugins: {
tailwindcss: {
config: './tailwind.config.ts',
},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
};
5 changes: 2 additions & 3 deletions apps/app-sandbox-react/src/global.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@config '../tailwind.config.ts';
6 changes: 2 additions & 4 deletions apps/app-sandbox-react/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// <reference types='vitest' />
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

Expand All @@ -13,10 +14,7 @@ export default defineConfig(() => ({
port: 4300,
host: 'localhost',
},
css: {
postcss: './postcss.config.js',
},
plugins: [react()],
plugins: [tailwindcss(), react()],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
Expand Down
6 changes: 0 additions & 6 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import nx from '@nx/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import { defineConfig, globalIgnores } from 'eslint/config';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import tailwind from 'eslint-plugin-tailwindcss';

export default defineConfig(
...nx.configs['flat/base'],
...nx.configs['flat/react'],
...nx.configs['flat/typescript'],
...nx.configs['flat/javascript'],
...tailwind.configs['flat/recommended'],
{
ignores: [
'**/dist',
Expand Down Expand Up @@ -44,10 +42,6 @@ export default defineConfig(
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
/**
* tailwind
*/
'tailwindcss/no-custom-classname': 'error',
/**
* typescript
*/
Expand Down
4 changes: 2 additions & 2 deletions libs/design-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
npm install @ledgerhq/lumen-design-core

# Install required peer dependency
npm install tailwindcss
npm install tailwindcss@^4.1.17 @tailwindcss/postcss
```

**Note:** Tailwind CSS v3.x is required as a peer dependency. Not compatible yet with Tailwind CSS v4.
**Note:** Tailwind CSS v4.x is the supported peer dependency.

## ⚡ Quick Setup

Expand Down
52 changes: 43 additions & 9 deletions libs/design-core/automations/designTokensEtl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ StyleDictionary.registerTransform({
name: 'name/custom/direct-css-var',
type: 'name',
transform: (token: TransformedToken) => {
return `--${token.path.join('-').toLowerCase()}`;
return token.path.join('-').toLowerCase();
},
});

Expand All @@ -31,6 +31,16 @@ function sanitizeTokenName(tokenName: string): string {
return newName;
}

const addPxUnitToNumber = (
value: string | number,
_tokenName: string,
): string | number => {
if (typeof value === 'number') {
return `${value}px`;
}
return value;
};

const filterPrimitives = (token: TransformedToken) =>
!token.filePath.includes('1.primitives.value.json');

Expand All @@ -50,11 +60,11 @@ StyleDictionary.registerFormat({
const output = { [mainKey]: {} };

dictionary.allTokens.forEach((token: TransformedToken) => {
const tokenName = sanitizeTokenName(token.name);
const finalTokenName = tokenName.replace(' ', '-');
const tokenName = sanitizeTokenName(token.name).replace(/ /g, '-');
const finalTokenName = `--${tokenName}`;
const tokenOriginalValue = token.original.$value;

let tokenFinalValue: string;
let tokenFinalValue: string | number;
if (
typeof tokenOriginalValue === 'string' &&
tokenOriginalValue.startsWith('{') &&
Expand All @@ -73,7 +83,7 @@ StyleDictionary.registerFormat({

tokenFinalValue = `var(${varName})`;
} else {
tokenFinalValue = tokenOriginalValue;
tokenFinalValue = addPxUnitToNumber(tokenOriginalValue, tokenName);
}

output[mainKey][finalTokenName] = tokenFinalValue;
Expand All @@ -96,6 +106,33 @@ export const tokens: ${tokensType} = ${JSON.stringify(output, null, 2)};`;
},
});

StyleDictionary.registerFormat({
name: 'css/custom-variables',
format: function ({ dictionary }) {
const variables = dictionary.allTokens
.map((token) => {
const name = sanitizeTokenName(token.name).replace(/ /g, '-');
let value = token.$value;

if (typeof value === 'number') {
value = value === 0 ? '0' : `${value}px`;
}

return `--${name}: ${value};`;
})
.join('\n');

return `/**
* Do not edit directly, this file was auto-generated.
*/

:root {
${variables}
}
`;
},
});

function getSDTypographyConfigForBreakpoint(breakpoint: string) {
const sources = [
`${tokensFolder}/1.primitives.value.json`,
Expand Down Expand Up @@ -200,10 +237,7 @@ function getSDPrimitivesConfig() {
files: [
{
destination: 'primitives.css',
format: 'css/variables',
options: {
outputReferences: true,
},
format: 'css/custom-variables',
},
],
actions: ['remove-default-suffix', 'prettier'],
Expand Down
2 changes: 1 addition & 1 deletion libs/design-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"!dist/figma/*"
],
"dependencies": {
"tailwindcss": "^3.4.0",
"tailwindcss": "^4.1.17",
"tslib": "^2.3.0"
}
}
6 changes: 4 additions & 2 deletions libs/design-core/src/presets/allBrands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
createShadowPlugin,
} from '../utils/index.js';

export const allBrandsPreset = {
export const allBrandsPreset: Config = {
content: [],
theme: {
boxShadow: {},
boxShadowColor: {},
fontSize: {},
fontWeight: {},
lineHeight: {},
colors: {},
},
plugins: [
createPrimitivesPlugin(),
Expand All @@ -26,4 +28,4 @@ export const allBrandsPreset = {
createAnimationsPlugin(),
],
darkMode: 'class',
} satisfies Config;
};
6 changes: 4 additions & 2 deletions libs/design-core/src/presets/enterprise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import {
createShadowPlugin,
} from '../utils/index.js';

export const enterprisePreset = {
export const enterprisePreset: Config = {
content: [],
theme: {
boxShadow: {},
boxShadowColor: {},
fontSize: {},
fontWeight: {},
lineHeight: {},
colors: {},
},
plugins: [
createPrimitivesPlugin(),
Expand All @@ -27,4 +29,4 @@ export const enterprisePreset = {
createAnimationsPlugin(),
],
darkMode: 'class',
} satisfies Config;
};
6 changes: 4 additions & 2 deletions libs/design-core/src/presets/ledger-live.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
createShadowPlugin,
} from '../utils/index.js';

export const ledgerLivePreset = {
export const ledgerLivePreset: Config = {
content: [],
theme: {
boxShadow: {},
boxShadowColor: {},
fontSize: {},
fontWeight: {},
lineHeight: {},
colors: {},
},
plugins: [
createPrimitivesPlugin(),
Expand All @@ -26,4 +28,4 @@ export const ledgerLivePreset = {
createAnimationsPlugin(),
],
darkMode: 'class',
} satisfies Config;
};
6 changes: 4 additions & 2 deletions libs/design-core/src/presets/websites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import {
createShadowPlugin,
} from '../utils/index.js';

export const websitesPreset = {
export const websitesPreset: Config = {
content: [],
theme: {
boxShadow: {},
boxShadowColor: {},
fontSize: {},
fontWeight: {},
lineHeight: {},
colors: {},
},
plugins: [
createPrimitivesPlugin(),
Expand All @@ -27,4 +29,4 @@ export const websitesPreset = {
createAnimationsPlugin(),
],
darkMode: 'class',
} satisfies Config;
};
Loading
Loading