Skip to content

Commit dfd2cb0

Browse files
mimarzCopilot
andauthored
feat: remove color categories (#4917)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 2064184 commit dfd2cb0

119 files changed

Lines changed: 4440 additions & 3855 deletions

File tree

Some content is hidden

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

.changeset/deep-brooms-rescue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@digdir/designsystemet": minor
3+
---
4+
5+
Removed color categories `main` and `support`. All colors are now defined in config file under `themes.<name>.colors` (just like neutral).

.changeset/rich-eggs-rest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@digdir/designsystemet": patch
3+
---
4+
5+
Increased minimum required `node` version 24.16.0.

.changeset/thirty-ends-live.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@digdir/designsystemet": minor
3+
---
4+
5+
Remove `UNSAFE_COLOR_GROUPS` env variable as its no longer relevant

.changeset/twelve-bugs-prove.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@digdir/designsystemet": minor
3+
---
4+
5+
Added support for auto-migrations which checks files and prompts users about needed migrations.

apps/themebuilder/app/_components/color-contrasts/color-contrasts.tsx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { useThemebuilder } from '~/routes/themebuilder/_utils/use-themebuilder';
1717
import classes from './color-contrasts.module.css';
1818

1919
const initialTheme = generateColorSchemes('#0062BA');
20-
const colorGroups = ['main', 'neutral', 'support'] as const;
2120

2221
export const ColorContrasts = () => {
2322
const { t } = useTranslation();
@@ -110,15 +109,13 @@ const ColorContrastMapper = ({
110109
const getMappedTheme = () => {
111110
const mappedColors: { [key: string]: Color } = {};
112111

113-
let colorTheme = colors?.main[0]?.colors || initialTheme;
112+
let colorTheme = colors?.[0]?.colors || initialTheme;
114113

115114
if (selectedColor !== 'dominant') {
116-
for (const group of colorGroups) {
117-
for (const color of colors[group]) {
118-
if (color.name === selectedColor) {
119-
colorTheme = color.colors;
120-
break;
121-
}
115+
for (const color of colors) {
116+
if (color.name === selectedColor) {
117+
colorTheme = color.colors;
118+
break;
122119
}
123120
}
124121

@@ -167,13 +164,11 @@ const ColorContrastMapper = ({
167164
aria-label={t('colorContrasts.select-color')}
168165
value={selectedColor}
169166
>
170-
{colorGroups.flatMap((group) =>
171-
colors[group as keyof typeof colors].map((color, colorIndex) => (
172-
<Select.Option key={`${group}-${colorIndex}`} value={color.name}>
173-
{color.name}
174-
</Select.Option>
175-
)),
176-
)}
167+
{colors.map((color, colorIndex) => (
168+
<Select.Option key={`color-${colorIndex}`} value={color.name}>
169+
{color.name}
170+
</Select.Option>
171+
))}
177172
{severityEnabled &&
178173
severityColors.map((color, index) => (
179174
<Select.Option key={`severity-${index}`} value={color.name}>

apps/themebuilder/app/_components/color-preview/color-preview.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,7 @@ export const ColorPreview = () => {
3030
useThemebuilder();
3131
const [view, setView] = useState<ViewType>(DEFAULT_VIEW);
3232

33-
const allColors = [
34-
...colors.main,
35-
...colors.neutral,
36-
...colors.support,
37-
...(severityEnabled ? severityColors : []),
38-
];
33+
const allColors = [...colors, ...(severityEnabled ? severityColors : [])];
3934

4035
const prepVariables = (variables: Record<string, string>) => {
4136
const prepped: Record<string, string> = {};
@@ -92,7 +87,7 @@ export const ColorPreview = () => {
9287
};
9388

9489
type CardProps = {
95-
color: ReturnType<typeof useThemebuilder>['colors']['main'][number];
90+
color: ReturnType<typeof useThemebuilder>['colors'][number];
9691
view?: ViewType;
9792
} & Omit<HTMLAttributes<HTMLDivElement>, 'color'>;
9893

apps/themebuilder/app/_components/color-variables.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,16 @@ export const ColorVariables = () => {
1212
const { colors, colorScheme, severityEnabled, severityColors } =
1313
useThemebuilder();
1414

15-
const neutralColor = colors?.neutral[0]?.hex || '';
16-
const [previewColor, setPreviewColor] = useState(colors?.main[0] || '');
15+
const neutralColor = colors?.find((c) => c.name === 'neutral')?.hex || '';
16+
const [previewColor, setPreviewColor] = useState(colors?.[0] || '');
1717

1818
const style = {
1919
...styleColorVars(neutralColor as CssColor, colorScheme, 'neutral'),
2020
...styleColorVars(previewColor.hex as CssColor, colorScheme),
2121
};
2222

2323
const allColors = [
24-
...colors.main,
25-
...colors.neutral,
26-
...colors.support,
24+
...(colors ?? []),
2725
...(severityEnabled ? severityColors : []),
2826
];
2927

@@ -40,7 +38,7 @@ export const ColorVariables = () => {
4038
const selectedColor = allColors.find(
4139
(c) => c.name === v.target.value,
4240
);
43-
setPreviewColor(selectedColor ?? colors.main[0]);
41+
setPreviewColor(selectedColor ?? colors[0]);
4442
}}
4543
>
4644
{allColors.map((color) => (

apps/themebuilder/app/_components/colors/colors.tsx

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ export const Colors = () => {
88

99
return (
1010
<div className={classes.rows}>
11-
<MainColors />
11+
<ThemeColors />
1212
<Divider />
1313
<NeutralColor />
14-
<Divider />
15-
<SupportColors />
1614
{severityEnabled && (
1715
<>
1816
<Divider />
@@ -23,57 +21,40 @@ export const Colors = () => {
2321
);
2422
};
2523

26-
const MainColors = () => {
27-
const {
28-
colors: { main },
29-
} = useThemebuilder();
24+
const ThemeColors = () => {
25+
const { colors } = useThemebuilder();
3026

3127
return (
3228
<div className={classes.rows}>
33-
{main.map((color, index) => (
34-
<div key={index} className={classes.row}>
35-
<div className={classes.scaleLabel}>{color.name}</div>
36-
<Scale
37-
colorScale={color.colors}
38-
showHeader={index === 0}
39-
namespace={color.name}
40-
/>
41-
</div>
42-
))}
29+
{colors
30+
.filter((c) => c.name !== 'neutral')
31+
.map((color, index) => (
32+
<div key={index} className={classes.row}>
33+
<div className={classes.scaleLabel}>{color.name}</div>
34+
<Scale
35+
colorScale={color.colors}
36+
showHeader={index === 0}
37+
namespace={color.name}
38+
/>
39+
</div>
40+
))}
4341
</div>
4442
);
4543
};
4644

4745
const NeutralColor = () => {
48-
const {
49-
colors: { neutral },
50-
} = useThemebuilder();
51-
52-
return (
53-
<div className={classes.rows}>
54-
{neutral.map((color, index) => (
55-
<div key={index} className={classes.row}>
56-
<div className={classes.scaleLabel}>{color.name}</div>
57-
<Scale colorScale={color.colors} namespace={color.name} />
58-
</div>
59-
))}
60-
</div>
61-
);
62-
};
63-
64-
const SupportColors = () => {
65-
const {
66-
colors: { support },
67-
} = useThemebuilder();
46+
const { colors } = useThemebuilder();
6847

6948
return (
7049
<div className={classes.rows}>
71-
{support.map((color, index) => (
72-
<div key={index} className={classes.row}>
73-
<div className={classes.scaleLabel}>{color.name}</div>
74-
<Scale colorScale={color.colors} namespace={color.name} />
75-
</div>
76-
))}
50+
{colors
51+
.filter((c) => c.name === 'neutral')
52+
.map((color, index) => (
53+
<div key={index} className={classes.row}>
54+
<div className={classes.scaleLabel}>{color.name}</div>
55+
<Scale colorScale={color.colors} namespace={color.name} />
56+
</div>
57+
))}
7758
</div>
7859
);
7960
};

apps/themebuilder/app/_components/config-paste/config-paste.tsx

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// biome-ignore-all lint/suspicious/noExplicitAny: we have not kept old schema for types, so we need to use any here
12
import { type ConfigSchema, configSchema } from '@digdir/designsystemet';
23
import {
34
Button,
@@ -31,7 +32,10 @@ export function ConfigPaste() {
3132
}
3233

3334
try {
34-
const parsed = JSON.parse(configText);
35+
const parsed = isConfigWithOldColorSchema(configText)
36+
? migrateConfigWithOldColorSchema(configText)
37+
: JSON.parse(configText);
38+
3539
const validated = configSchema.parse(parsed);
3640
setValidatedConfig(validated);
3741

@@ -107,29 +111,9 @@ export function ConfigPaste() {
107111
{themeName}
108112
</Paragraph>
109113
<div className={classes.colorPreview}>
110-
{themeConfig.colors.main &&
111-
Object.values(themeConfig.colors.main)
112-
.slice(0, 3)
113-
.map((color, idx) => (
114-
<div
115-
key={idx}
116-
className={classes.colorDot}
117-
style={{ backgroundColor: color }}
118-
title={color}
119-
/>
120-
))}
121-
{themeConfig.colors.neutral && (
122-
<div
123-
className={classes.colorDot}
124-
style={{
125-
backgroundColor: themeConfig.colors.neutral,
126-
}}
127-
title={themeConfig.colors.neutral}
128-
/>
129-
)}
130-
{themeConfig.colors?.support &&
131-
Object.values(themeConfig.colors.support)
132-
.slice(0, 3)
114+
{themeConfig.colors &&
115+
Object.values(themeConfig.colors)
116+
.slice(0, 7)
133117
.map((color, idx) => (
134118
<div
135119
key={idx}
@@ -160,3 +144,59 @@ export function ConfigPaste() {
160144
</div>
161145
);
162146
}
147+
148+
// Temporary migration functions for old color schema, to be removed in future versions
149+
const isOldColorSchema = (theme: any): boolean => {
150+
return (
151+
theme.colors &&
152+
Object.keys(theme.colors).length === 3 &&
153+
theme.colors.main &&
154+
theme.colors.support &&
155+
theme.colors.neutral
156+
);
157+
};
158+
159+
const isConfigWithOldColorSchema = (config: any): boolean => {
160+
const currentConfig = JSON.parse(config);
161+
162+
if (!currentConfig.themes) {
163+
return false;
164+
}
165+
166+
return Object.values(currentConfig.themes).some(isOldColorSchema);
167+
};
168+
169+
const migrateConfigWithOldColorSchema = (config: string): string => {
170+
const currentConfig = JSON.parse(config);
171+
const updatedThemes: Record<string, any> = {};
172+
173+
if (currentConfig.themes) {
174+
for (const [themeName, _] of Object.entries(currentConfig.themes)) {
175+
const theme = currentConfig.themes[themeName];
176+
177+
if (isOldColorSchema(theme)) {
178+
const { main, support, neutral, ...restColors } = theme.colors;
179+
180+
const updatedTheme = {
181+
...theme,
182+
colors: {
183+
...restColors,
184+
...main,
185+
...support,
186+
neutral,
187+
},
188+
};
189+
190+
updatedThemes[themeName] = updatedTheme;
191+
} else {
192+
updatedThemes[themeName] = theme;
193+
}
194+
}
195+
}
196+
const migratedConfig = {
197+
...currentConfig,
198+
themes: updatedThemes,
199+
};
200+
201+
return migratedConfig;
202+
};

apps/themebuilder/app/_components/examples-components/examples-components.tsx

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,9 @@ export const ExamplesComponents = ({
5454
const ref = useRef<HTMLDivElement>(null);
5555
const { colors } = useThemebuilder();
5656

57-
const neutralColor = colors?.neutral[0].hex || '#F5F7FA';
58-
const [previewColor, setPreviewColor] = useState(
59-
colors?.main[0].hex || color,
60-
);
57+
const neutralColor =
58+
colors?.find((c) => c.name === 'neutral')?.hex || '#F5F7FA';
59+
const [previewColor, setPreviewColor] = useState(colors?.[0]?.hex || color);
6160

6261
useEffect(() => {
6362
if (ref.current) {
@@ -74,7 +73,7 @@ export const ExamplesComponents = ({
7473

7574
useEffect(() => {
7675
if (!colors) return;
77-
const allColors = [...colors.main, ...colors.support];
76+
const allColors = colors.filter((c) => c.name !== 'neutral');
7877
/* if select colors is gone, set to default */
7978
if (!allColors.find((c) => c.hex === previewColor)) {
8079
setPreviewColor(color);
@@ -97,27 +96,24 @@ export const ExamplesComponents = ({
9796
value={previewColor}
9897
onChange={(v) => {
9998
if (!colors) return;
100-
const allColors = [...colors.main, ...colors.support];
99+
const allColors = colors.filter((c) => c.name !== 'neutral');
101100
/* find the selected color */
102101
let selected = allColors.find(
103102
(c) => c.hex === v.target.value,
104103
)?.hex;
105104
if (!selected) {
106-
selected = colors.main[0].hex;
105+
selected = allColors[0]?.hex;
107106
}
108107
setPreviewColor(selected as CssColor);
109108
}}
110109
>
111-
{colors.main.map((color) => (
112-
<Select.Option key={color.name} value={color.hex}>
113-
{color.name}
114-
</Select.Option>
115-
))}
116-
{colors.support.map((color) => (
117-
<Select.Option key={color.name} value={color.hex}>
118-
{color.name}
119-
</Select.Option>
120-
))}
110+
{colors
111+
.filter((c) => c.name !== 'neutral')
112+
.map((color) => (
113+
<Select.Option key={color.name} value={color.hex}>
114+
{color.name}
115+
</Select.Option>
116+
))}
121117
</Select>
122118
</Field>
123119

0 commit comments

Comments
 (0)