(web): dashboard enhancements

This commit is contained in:
Marcel Arndt
2025-06-05 15:24:05 +02:00
parent d8a2930247
commit 44f627499a
123 changed files with 8890 additions and 2052 deletions
@@ -0,0 +1,419 @@
{
"description": "TYPE: CUSTOM\nMaterial Theme Builder export 2025-04-22 04:44:20",
"seed": "#346DBB",
"coreColors": {
"primary": "#346DBB",
"tertiary": "#AE90C2"
},
"extendedColors": [],
"schemes": {
"light": {
"primary": "#0D54A1",
"surfaceTint": "#205EAB",
"onPrimary": "#FFFFFF",
"primaryContainer": "#346DBB",
"onPrimaryContainer": "#EBF0FF",
"secondary": "#4D5F7E",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#C8DBFF",
"onSecondaryContainer": "#4E607F",
"tertiary": "#6E5482",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#AE90C2",
"onTertiaryContainer": "#412953",
"error": "#BA1A1A",
"onError": "#FFFFFF",
"errorContainer": "#FFDAD6",
"onErrorContainer": "#93000A",
"background": "#F9F9FF",
"onBackground": "#191C21",
"surface": "#F9F9FF",
"onSurface": "#191C21",
"surfaceVariant": "#DEE2EF",
"onSurfaceVariant": "#424751",
"outline": "#727782",
"outlineVariant": "#C2C6D3",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2E3036",
"inverseOnSurface": "#F0F0F7",
"inversePrimary": "#A9C7FF",
"primaryFixed": "#D6E3FF",
"onPrimaryFixed": "#001B3D",
"primaryFixedDim": "#A9C7FF",
"onPrimaryFixedVariant": "#00468B",
"secondaryFixed": "#D6E3FF",
"onSecondaryFixed": "#061B38",
"secondaryFixedDim": "#B5C7EB",
"onSecondaryFixedVariant": "#354765",
"tertiaryFixed": "#F2DAFF",
"onTertiaryFixed": "#28103A",
"tertiaryFixedDim": "#DBBBEF",
"onTertiaryFixedVariant": "#563C68",
"surfaceDim": "#D9D9E1",
"surfaceBright": "#F9F9FF",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F3F3FA",
"surfaceContainer": "#EDEDF5",
"surfaceContainerHigh": "#E7E8EF",
"surfaceContainerHighest": "#E1E2E9"
},
"light-medium-contrast": {
"primary": "#00366D",
"surfaceTint": "#205EAB",
"onPrimary": "#FFFFFF",
"primaryContainer": "#346DBB",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#243654",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#5C6E8E",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#442C57",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#7E6291",
"onTertiaryContainer": "#FFFFFF",
"error": "#740006",
"onError": "#FFFFFF",
"errorContainer": "#CF2C27",
"onErrorContainer": "#FFFFFF",
"background": "#F9F9FF",
"onBackground": "#191C21",
"surface": "#F9F9FF",
"onSurface": "#0F1116",
"surfaceVariant": "#DEE2EF",
"onSurfaceVariant": "#313640",
"outline": "#4E525D",
"outlineVariant": "#686D78",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2E3036",
"inverseOnSurface": "#F0F0F7",
"inversePrimary": "#A9C7FF",
"primaryFixed": "#346DBB",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#0D54A1",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#5C6E8E",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#435574",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#7E6291",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#644A77",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#C5C6CD",
"surfaceBright": "#F9F9FF",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F3F3FA",
"surfaceContainer": "#E7E8EF",
"surfaceContainerHigh": "#DCDCE3",
"surfaceContainerHighest": "#D0D1D8"
},
"light-high-contrast": {
"primary": "#002C5B",
"surfaceTint": "#205EAB",
"onPrimary": "#FFFFFF",
"primaryContainer": "#00488F",
"onPrimaryContainer": "#FFFFFF",
"secondary": "#1A2C49",
"onSecondary": "#FFFFFF",
"secondaryContainer": "#384A68",
"onSecondaryContainer": "#FFFFFF",
"tertiary": "#3A224C",
"onTertiary": "#FFFFFF",
"tertiaryContainer": "#583F6B",
"onTertiaryContainer": "#FFFFFF",
"error": "#600004",
"onError": "#FFFFFF",
"errorContainer": "#98000A",
"onErrorContainer": "#FFFFFF",
"background": "#F9F9FF",
"onBackground": "#191C21",
"surface": "#F9F9FF",
"onSurface": "#000000",
"surfaceVariant": "#DEE2EF",
"onSurfaceVariant": "#000000",
"outline": "#272C36",
"outlineVariant": "#444954",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#2E3036",
"inverseOnSurface": "#FFFFFF",
"inversePrimary": "#A9C7FF",
"primaryFixed": "#00488F",
"onPrimaryFixed": "#FFFFFF",
"primaryFixedDim": "#003267",
"onPrimaryFixedVariant": "#FFFFFF",
"secondaryFixed": "#384A68",
"onSecondaryFixed": "#FFFFFF",
"secondaryFixedDim": "#213350",
"onSecondaryFixedVariant": "#FFFFFF",
"tertiaryFixed": "#583F6B",
"onTertiaryFixed": "#FFFFFF",
"tertiaryFixedDim": "#402853",
"onTertiaryFixedVariant": "#FFFFFF",
"surfaceDim": "#B7B8BF",
"surfaceBright": "#F9F9FF",
"surfaceContainerLowest": "#FFFFFF",
"surfaceContainerLow": "#F0F0F7",
"surfaceContainer": "#E1E2E9",
"surfaceContainerHigh": "#D3D4DB",
"surfaceContainerHighest": "#C5C6CD"
},
"dark": {
"primary": "#A9C7FF",
"surfaceTint": "#A9C7FF",
"onPrimary": "#003062",
"primaryContainer": "#346DBB",
"onPrimaryContainer": "#EBF0FF",
"secondary": "#B5C7EB",
"onSecondary": "#1E314E",
"secondaryContainer": "#384968",
"onSecondaryContainer": "#A7B9DD",
"tertiary": "#DBBBEF",
"onTertiary": "#3E2650",
"tertiaryContainer": "#AE90C2",
"onTertiaryContainer": "#412953",
"error": "#FFB4AB",
"onError": "#690005",
"errorContainer": "#93000A",
"onErrorContainer": "#FFDAD6",
"background": "#111318",
"onBackground": "#E1E2E9",
"surface": "#111318",
"onSurface": "#E1E2E9",
"surfaceVariant": "#424751",
"onSurfaceVariant": "#C2C6D3",
"outline": "#8C919C",
"outlineVariant": "#424751",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E1E2E9",
"inverseOnSurface": "#2E3036",
"inversePrimary": "#205EAB",
"primaryFixed": "#D6E3FF",
"onPrimaryFixed": "#001B3D",
"primaryFixedDim": "#A9C7FF",
"onPrimaryFixedVariant": "#00468B",
"secondaryFixed": "#D6E3FF",
"onSecondaryFixed": "#061B38",
"secondaryFixedDim": "#B5C7EB",
"onSecondaryFixedVariant": "#354765",
"tertiaryFixed": "#F2DAFF",
"onTertiaryFixed": "#28103A",
"tertiaryFixedDim": "#DBBBEF",
"onTertiaryFixedVariant": "#563C68",
"surfaceDim": "#111318",
"surfaceBright": "#37393F",
"surfaceContainerLowest": "#0C0E13",
"surfaceContainerLow": "#191C21",
"surfaceContainer": "#1D2025",
"surfaceContainerHigh": "#282A2F",
"surfaceContainerHighest": "#32353A"
},
"dark-medium-contrast": {
"primary": "#CCDDFF",
"surfaceTint": "#A9C7FF",
"onPrimary": "#00254F",
"primaryContainer": "#5D91E2",
"onPrimaryContainer": "#000000",
"secondary": "#CCDDFF",
"onSecondary": "#122642",
"secondaryContainer": "#7F91B3",
"onSecondaryContainer": "#000000",
"tertiary": "#EFD2FF",
"onTertiary": "#331B45",
"tertiaryContainer": "#AE90C2",
"onTertiaryContainer": "#17012A",
"error": "#FFD2CC",
"onError": "#540003",
"errorContainer": "#FF5449",
"onErrorContainer": "#000000",
"background": "#111318",
"onBackground": "#E1E2E9",
"surface": "#111318",
"onSurface": "#FFFFFF",
"surfaceVariant": "#424751",
"onSurfaceVariant": "#D8DCE9",
"outline": "#ADB2BE",
"outlineVariant": "#8C909C",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E1E2E9",
"inverseOnSurface": "#282A2F",
"inversePrimary": "#00478D",
"primaryFixed": "#D6E3FF",
"onPrimaryFixed": "#00112A",
"primaryFixedDim": "#A9C7FF",
"onPrimaryFixedVariant": "#00366D",
"secondaryFixed": "#D6E3FF",
"onSecondaryFixed": "#00112A",
"secondaryFixedDim": "#B5C7EB",
"onSecondaryFixedVariant": "#243654",
"tertiaryFixed": "#F2DAFF",
"onTertiaryFixed": "#1D052F",
"tertiaryFixedDim": "#DBBBEF",
"onTertiaryFixedVariant": "#442C57",
"surfaceDim": "#111318",
"surfaceBright": "#42444A",
"surfaceContainerLowest": "#05070C",
"surfaceContainerLow": "#1B1E23",
"surfaceContainer": "#26282D",
"surfaceContainerHigh": "#303338",
"surfaceContainerHighest": "#3B3E43"
},
"dark-high-contrast": {
"primary": "#EBF0FF",
"surfaceTint": "#A9C7FF",
"onPrimary": "#000000",
"primaryContainer": "#A2C4FF",
"onPrimaryContainer": "#000B1F",
"secondary": "#EBF0FF",
"onSecondary": "#000000",
"secondaryContainer": "#B1C3E7",
"onSecondaryContainer": "#000B1F",
"tertiary": "#FAEBFF",
"onTertiary": "#000000",
"tertiaryContainer": "#D7B7EB",
"onTertiaryContainer": "#160029",
"error": "#FFECE9",
"onError": "#000000",
"errorContainer": "#FFAEA4",
"onErrorContainer": "#220001",
"background": "#111318",
"onBackground": "#E1E2E9",
"surface": "#111318",
"onSurface": "#FFFFFF",
"surfaceVariant": "#424751",
"onSurfaceVariant": "#FFFFFF",
"outline": "#ECF0FD",
"outlineVariant": "#BEC2CF",
"shadow": "#000000",
"scrim": "#000000",
"inverseSurface": "#E1E2E9",
"inverseOnSurface": "#000000",
"inversePrimary": "#00478D",
"primaryFixed": "#D6E3FF",
"onPrimaryFixed": "#000000",
"primaryFixedDim": "#A9C7FF",
"onPrimaryFixedVariant": "#00112A",
"secondaryFixed": "#D6E3FF",
"onSecondaryFixed": "#000000",
"secondaryFixedDim": "#B5C7EB",
"onSecondaryFixedVariant": "#00112A",
"tertiaryFixed": "#F2DAFF",
"onTertiaryFixed": "#000000",
"tertiaryFixedDim": "#DBBBEF",
"onTertiaryFixedVariant": "#1D052F",
"surfaceDim": "#111318",
"surfaceBright": "#4E5056",
"surfaceContainerLowest": "#000000",
"surfaceContainerLow": "#1D2025",
"surfaceContainer": "#2E3036",
"surfaceContainerHigh": "#393B41",
"surfaceContainerHighest": "#45474C"
}
},
"palettes": {
"primary": {
"0": "#000000",
"5": "#00112A",
"10": "#001B3D",
"15": "#00254F",
"20": "#003062",
"25": "#003B76",
"30": "#00468B",
"35": "#07529E",
"40": "#205EAB",
"50": "#4077C6",
"60": "#5D91E2",
"70": "#78ACFF",
"80": "#A9C7FF",
"90": "#D6E3FF",
"95": "#ECF0FF",
"98": "#F9F9FF",
"99": "#FDFBFF",
"100": "#FFFFFF"
},
"secondary": {
"0": "#000000",
"5": "#071121",
"10": "#121C2C",
"15": "#1C2637",
"20": "#273142",
"25": "#323C4E",
"30": "#3D4759",
"35": "#495366",
"40": "#555F72",
"50": "#6D778B",
"60": "#8791A6",
"70": "#A1ABC1",
"80": "#BDC7DD",
"90": "#D9E3FA",
"95": "#ECF0FF",
"98": "#F9F9FF",
"99": "#FDFBFF",
"100": "#FFFFFF"
},
"tertiary": {
"0": "#000000",
"5": "#1C052F",
"10": "#28103A",
"15": "#331B45",
"20": "#3E2650",
"25": "#4A315C",
"30": "#563C68",
"35": "#624875",
"40": "#6E5482",
"50": "#886C9C",
"60": "#A386B7",
"70": "#BFA0D3",
"80": "#DBBBEF",
"90": "#F2DAFF",
"95": "#FBECFF",
"98": "#FFF7FD",
"99": "#FFFBFF",
"100": "#FFFFFF"
},
"neutral": {
"0": "#000000",
"5": "#101114",
"10": "#1A1B1E",
"15": "#252629",
"20": "#2F3033",
"25": "#3A3B3F",
"30": "#46474A",
"35": "#525256",
"40": "#5E5E62",
"50": "#77777A",
"60": "#909094",
"70": "#ABABAF",
"80": "#C7C6CA",
"90": "#E3E2E6",
"95": "#F1F0F4",
"98": "#FAF9FD",
"99": "#FDFBFF",
"100": "#FFFFFF"
},
"neutral-variant": {
"0": "#000000",
"5": "#0E1117",
"10": "#181C22",
"15": "#22262D",
"20": "#2D3038",
"25": "#383B43",
"30": "#43474E",
"35": "#4F525A",
"40": "#5B5E66",
"50": "#74777F",
"60": "#8E9099",
"70": "#A8ABB4",
"80": "#C4C6CF",
"90": "#E0E2EC",
"95": "#EEF0FA",
"98": "#F9F9FF",
"99": "#FDFBFF",
"100": "#FFFFFF"
}
}
}
@@ -0,0 +1,135 @@
// This file was generated by running 'ng generate @angular/material:theme-color'.
// Proceed with caution if making changes to this file.
@use 'sass:map';
@use '@angular/material' as mat;
$_palettes: (
primary: (
0: #000000,
10: #001453,
20: #002584,
25: #042F9B,
30: #1B3CA7,
35: #2B49B3,
40: #3956BF,
50: #546FDA,
60: #6F8AF6,
70: #92A6FF,
80: #B7C4FF,
90: #DDE1FF,
95: #EFEFFF,
98: #FBF8FF,
99: #FEFBFF,
100: #FFFFFF,
),
secondary: (
0: #000000,
10: #141A35,
20: #292F4B,
25: #343A57,
30: #3F4563,
35: #4B516F,
40: #575D7C,
50: #6F7596,
60: #898FB0,
70: #A4A9CC,
80: #BFC5E8,
90: #DDE1FF,
95: #F0EFFF,
98: #FBF8FF,
99: #FEFBFF,
100: #FFFFFF,
),
tertiary: (
0: #000000,
10: #001E2B,
20: #003548,
25: #004157,
30: #004D67,
35: #005977,
40: #006687,
50: #0080A9,
60: #009CCD,
70: #26B8EE,
80: #74D1FF,
90: #C1E8FF,
95: #E2F3FF,
98: #F4FAFF,
99: #FBFCFF,
100: #FFFFFF
),
neutral: (
0: #000000,
10: #1B1B1F,
20: #303034,
25: #3B3B3F,
30: #47464A,
35: #525256,
40: #5F5E62,
50: #77767A,
60: #919094,
70: #ACAAAF,
80: #C8C6CA,
90: #E4E1E6,
95: #F2F0F4,
98: #FBF8FD,
99: #FEFBFF,
100: #FFFFFF,
4: #0c0e15,
6: #12131b,
12: #1e1f27,
17: #282932,
22: #33343d,
24: #383942,
87: #dad9e4,
92: #e8e7f3,
94: #eeedf8,
96: #f4f2fe,
),
neutral-variant: (
0: #000000,
10: #1A1B23,
20: #2F3038,
25: #3A3B43,
30: #45464F,
35: #51525B,
40: #5D5E67,
50: #767680,
60: #90909A,
70: #ABAAB4,
80: #C6C5D0,
90: #E2E1EC,
95: #F1F0FA,
98: #FBF8FF,
99: #FEFBFF,
100: #FFFFFF
),
error: (
0: #000000,
10: #2d0102,
20: #690005,
25: #7d0006,
30: #93000a,
35: #a8070f,
40: #ba1a1a,
50: #de372f,
60: #ff544a,
70: #ff897d,
80: #feb4ab,
90: #ffdad7,
95: #ffede9,
98: #fff8f6,
99: #fffbff,
100: #ffffff,
),
);
$_rest: (
secondary: map.get($_palettes, secondary),
neutral: map.get($_palettes, neutral),
neutral-variant: map.get($_palettes, neutral-variant),
error: map.get($_palettes, error),
);
$primary-palette: map.merge(map.get($_palettes, primary), $_rest);
$tertiary-palette: map.merge(map.get($_palettes, tertiary), $_rest);
@@ -0,0 +1,399 @@
//
//
// 𝗖 𝗢 𝗟 𝗢 𝗥
// v 1.9.1
//
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// General
// ───────────────────────────────────
$oc-white: #ffffff;
$oc-black: #000000;
// Gray
// ───────────────────────────────────
$oc-gray-list: (
"0": #f8f9fa,
"1": #f1f3f5,
"2": #e9ecef,
"3": #dee2e6,
"4": #ced4da,
"5": #adb5bd,
"6": #868e96,
"7": #495057,
"8": #343a40,
"9": #212529
);
$oc-gray-0: map-get($oc-gray-list, "0");
$oc-gray-1: map-get($oc-gray-list, "1");
$oc-gray-2: map-get($oc-gray-list, "2");
$oc-gray-3: map-get($oc-gray-list, "3");
$oc-gray-4: map-get($oc-gray-list, "4");
$oc-gray-5: map-get($oc-gray-list, "5");
$oc-gray-6: map-get($oc-gray-list, "6");
$oc-gray-7: map-get($oc-gray-list, "7");
$oc-gray-8: map-get($oc-gray-list, "8");
$oc-gray-9: map-get($oc-gray-list, "9");
// Red
// ───────────────────────────────────
$oc-red-list: (
"0": #fff5f5,
"1": #ffe3e3,
"2": #ffc9c9,
"3": #ffa8a8,
"4": #ff8787,
"5": #ff6b6b,
"6": #fa5252,
"7": #f03e3e,
"8": #e03131,
"9": #c92a2a
);
$oc-red-0: map-get($oc-red-list, "0");
$oc-red-1: map-get($oc-red-list, "1");
$oc-red-2: map-get($oc-red-list, "2");
$oc-red-3: map-get($oc-red-list, "3");
$oc-red-4: map-get($oc-red-list, "4");
$oc-red-5: map-get($oc-red-list, "5");
$oc-red-6: map-get($oc-red-list, "6");
$oc-red-7: map-get($oc-red-list, "7");
$oc-red-8: map-get($oc-red-list, "8");
$oc-red-9: map-get($oc-red-list, "9");
// Pink
// ───────────────────────────────────
$oc-pink-list: (
"0": #fff0f6,
"1": #ffdeeb,
"2": #fcc2d7,
"3": #faa2c1,
"4": #f783ac,
"5": #f06595,
"6": #e64980,
"7": #d6336c,
"8": #c2255c,
"9": #a61e4d
);
$oc-pink-0: map-get($oc-pink-list, "0");
$oc-pink-1: map-get($oc-pink-list, "1");
$oc-pink-2: map-get($oc-pink-list, "2");
$oc-pink-3: map-get($oc-pink-list, "3");
$oc-pink-4: map-get($oc-pink-list, "4");
$oc-pink-5: map-get($oc-pink-list, "5");
$oc-pink-6: map-get($oc-pink-list, "6");
$oc-pink-7: map-get($oc-pink-list, "7");
$oc-pink-8: map-get($oc-pink-list, "8");
$oc-pink-9: map-get($oc-pink-list, "9");
// Grape
// ───────────────────────────────────
$oc-grape-list: (
"0": #f8f0fc,
"1": #f3d9fa,
"2": #eebefa,
"3": #e599f7,
"4": #da77f2,
"5": #cc5de8,
"6": #be4bdb,
"7": #ae3ec9,
"8": #9c36b5,
"9": #862e9c
);
$oc-grape-0: map-get($oc-grape-list, "0");
$oc-grape-1: map-get($oc-grape-list, "1");
$oc-grape-2: map-get($oc-grape-list, "2");
$oc-grape-3: map-get($oc-grape-list, "3");
$oc-grape-4: map-get($oc-grape-list, "4");
$oc-grape-5: map-get($oc-grape-list, "5");
$oc-grape-6: map-get($oc-grape-list, "6");
$oc-grape-7: map-get($oc-grape-list, "7");
$oc-grape-8: map-get($oc-grape-list, "8");
$oc-grape-9: map-get($oc-grape-list, "9");
// Violet
// ───────────────────────────────────
$oc-violet-list: (
"0": #f3f0ff,
"1": #e5dbff,
"2": #d0bfff,
"3": #b197fc,
"4": #9775fa,
"5": #845ef7,
"6": #7950f2,
"7": #7048e8,
"8": #6741d9,
"9": #5f3dc4
);
$oc-violet-0: map-get($oc-violet-list, "0");
$oc-violet-1: map-get($oc-violet-list, "1");
$oc-violet-2: map-get($oc-violet-list, "2");
$oc-violet-3: map-get($oc-violet-list, "3");
$oc-violet-4: map-get($oc-violet-list, "4");
$oc-violet-5: map-get($oc-violet-list, "5");
$oc-violet-6: map-get($oc-violet-list, "6");
$oc-violet-7: map-get($oc-violet-list, "7");
$oc-violet-8: map-get($oc-violet-list, "8");
$oc-violet-9: map-get($oc-violet-list, "9");
// Indigo
// ───────────────────────────────────
$oc-indigo-list: (
"0": #edf2ff,
"1": #dbe4ff,
"2": #bac8ff,
"3": #91a7ff,
"4": #748ffc,
"5": #5c7cfa,
"6": #4c6ef5,
"7": #4263eb,
"8": #3b5bdb,
"9": #364fc7
);
$oc-indigo-0: map-get($oc-indigo-list, "0");
$oc-indigo-1: map-get($oc-indigo-list, "1");
$oc-indigo-2: map-get($oc-indigo-list, "2");
$oc-indigo-3: map-get($oc-indigo-list, "3");
$oc-indigo-4: map-get($oc-indigo-list, "4");
$oc-indigo-5: map-get($oc-indigo-list, "5");
$oc-indigo-6: map-get($oc-indigo-list, "6");
$oc-indigo-7: map-get($oc-indigo-list, "7");
$oc-indigo-8: map-get($oc-indigo-list, "8");
$oc-indigo-9: map-get($oc-indigo-list, "9");
// Blue
// ───────────────────────────────────
$oc-blue-list: (
"0": #e7f5ff,
"1": #d0ebff,
"2": #a5d8ff,
"3": #74c0fc,
"4": #4dabf7,
"5": #339af0,
"6": #228be6,
"7": #1c7ed6,
"8": #1971c2,
"9": #1864ab
);
$oc-blue-0: map-get($oc-blue-list, "0");
$oc-blue-1: map-get($oc-blue-list, "1");
$oc-blue-2: map-get($oc-blue-list, "2");
$oc-blue-3: map-get($oc-blue-list, "3");
$oc-blue-4: map-get($oc-blue-list, "4");
$oc-blue-5: map-get($oc-blue-list, "5");
$oc-blue-6: map-get($oc-blue-list, "6");
$oc-blue-7: map-get($oc-blue-list, "7");
$oc-blue-8: map-get($oc-blue-list, "8");
$oc-blue-9: map-get($oc-blue-list, "9");
// Cyan
// ───────────────────────────────────
$oc-cyan-list: (
"0": #e3fafc,
"1": #c5f6fa,
"2": #99e9f2,
"3": #66d9e8,
"4": #3bc9db,
"5": #22b8cf,
"6": #15aabf,
"7": #1098ad,
"8": #0c8599,
"9": #0b7285
);
$oc-cyan-0: map-get($oc-cyan-list, "0");
$oc-cyan-1: map-get($oc-cyan-list, "1");
$oc-cyan-2: map-get($oc-cyan-list, "2");
$oc-cyan-3: map-get($oc-cyan-list, "3");
$oc-cyan-4: map-get($oc-cyan-list, "4");
$oc-cyan-5: map-get($oc-cyan-list, "5");
$oc-cyan-6: map-get($oc-cyan-list, "6");
$oc-cyan-7: map-get($oc-cyan-list, "7");
$oc-cyan-8: map-get($oc-cyan-list, "8");
$oc-cyan-9: map-get($oc-cyan-list, "9");
// Teal
// ───────────────────────────────────
$oc-teal-list: (
"0": #e6fcf5,
"1": #c3fae8,
"2": #96f2d7,
"3": #63e6be,
"4": #38d9a9,
"5": #20c997,
"6": #12b886,
"7": #0ca678,
"8": #099268,
"9": #087f5b
);
$oc-teal-0: map-get($oc-teal-list, "0");
$oc-teal-1: map-get($oc-teal-list, "1");
$oc-teal-2: map-get($oc-teal-list, "2");
$oc-teal-3: map-get($oc-teal-list, "3");
$oc-teal-4: map-get($oc-teal-list, "4");
$oc-teal-5: map-get($oc-teal-list, "5");
$oc-teal-6: map-get($oc-teal-list, "6");
$oc-teal-7: map-get($oc-teal-list, "7");
$oc-teal-8: map-get($oc-teal-list, "8");
$oc-teal-9: map-get($oc-teal-list, "9");
// Green
// ───────────────────────────────────
$oc-green-list: (
"0": #ebfbee,
"1": #d3f9d8,
"2": #b2f2bb,
"3": #8ce99a,
"4": #69db7c,
"5": #51cf66,
"6": #40c057,
"7": #37b24d,
"8": #2f9e44,
"9": #2b8a3e
);
$oc-green-0: map-get($oc-green-list, "0");
$oc-green-1: map-get($oc-green-list, "1");
$oc-green-2: map-get($oc-green-list, "2");
$oc-green-3: map-get($oc-green-list, "3");
$oc-green-4: map-get($oc-green-list, "4");
$oc-green-5: map-get($oc-green-list, "5");
$oc-green-6: map-get($oc-green-list, "6");
$oc-green-7: map-get($oc-green-list, "7");
$oc-green-8: map-get($oc-green-list, "8");
$oc-green-9: map-get($oc-green-list, "9");
// Lime
// ───────────────────────────────────
$oc-lime-list: (
"0": #f4fce3,
"1": #e9fac8,
"2": #d8f5a2,
"3": #c0eb75,
"4": #a9e34b,
"5": #94d82d,
"6": #82c91e,
"7": #74b816,
"8": #66a80f,
"9": #5c940d
);
$oc-lime-0: map-get($oc-lime-list, "0");
$oc-lime-1: map-get($oc-lime-list, "1");
$oc-lime-2: map-get($oc-lime-list, "2");
$oc-lime-3: map-get($oc-lime-list, "3");
$oc-lime-4: map-get($oc-lime-list, "4");
$oc-lime-5: map-get($oc-lime-list, "5");
$oc-lime-6: map-get($oc-lime-list, "6");
$oc-lime-7: map-get($oc-lime-list, "7");
$oc-lime-8: map-get($oc-lime-list, "8");
$oc-lime-9: map-get($oc-lime-list, "9");
// Yellow
// ───────────────────────────────────
$oc-yellow-list: (
"0": #fff9db,
"1": #fff3bf,
"2": #ffec99,
"3": #ffe066,
"4": #ffd43b,
"5": #fcc419,
"6": #fab005,
"7": #f59f00,
"8": #f08c00,
"9": #e67700
);
$oc-yellow-0: map-get($oc-yellow-list, "0");
$oc-yellow-1: map-get($oc-yellow-list, "1");
$oc-yellow-2: map-get($oc-yellow-list, "2");
$oc-yellow-3: map-get($oc-yellow-list, "3");
$oc-yellow-4: map-get($oc-yellow-list, "4");
$oc-yellow-5: map-get($oc-yellow-list, "5");
$oc-yellow-6: map-get($oc-yellow-list, "6");
$oc-yellow-7: map-get($oc-yellow-list, "7");
$oc-yellow-8: map-get($oc-yellow-list, "8");
$oc-yellow-9: map-get($oc-yellow-list, "9");
// Orange
// ───────────────────────────────────
$oc-orange-list: (
"0": #fff4e6,
"1": #ffe8cc,
"2": #ffd8a8,
"3": #ffc078,
"4": #ffa94d,
"5": #ff922b,
"6": #fd7e14,
"7": #f76707,
"8": #e8590c,
"9": #d9480f
);
$oc-orange-0: map-get($oc-orange-list, "0");
$oc-orange-1: map-get($oc-orange-list, "1");
$oc-orange-2: map-get($oc-orange-list, "2");
$oc-orange-3: map-get($oc-orange-list, "3");
$oc-orange-4: map-get($oc-orange-list, "4");
$oc-orange-5: map-get($oc-orange-list, "5");
$oc-orange-6: map-get($oc-orange-list, "6");
$oc-orange-7: map-get($oc-orange-list, "7");
$oc-orange-8: map-get($oc-orange-list, "8");
$oc-orange-9: map-get($oc-orange-list, "9");
// Color list
// ───────────────────────────────────
$oc-color-spectrum: 9;
$oc-color-list: (
$oc-gray-list: "gray",
$oc-red-list: "red",
$oc-pink-list: "pink",
$oc-grape-list: "grape",
$oc-violet-list: "violet",
$oc-indigo-list: "indigo",
$oc-blue-list: "blue",
$oc-cyan-list: "cyan",
$oc-teal-list: "teal",
$oc-green-list: "green",
$oc-lime-list: "lime",
$oc-yellow-list: "yellow",
$oc-orange-list: "orange"
);
@@ -0,0 +1,51 @@
import { tailwindThemeFromColor } from "./tailwindThemeFromColor";
import tailwindMaterialSurfaces from "./packages/tailwind-material-surface";
import withModeAwareColors from "./packages/with-mode-aware-colors";
export const withMaterialColors = (
config,
colorsMap,
options = { scheme: "content", contrast: 0, extend: false }
) => {
if (colorsMap.primary) {
const materialColors = tailwindThemeFromColor(
colorsMap,
options?.scheme || "content",
options?.contrast || 0
);
return withModeAwareColors({
...config,
theme: options.extend
? {
...(config.theme || {}),
extend: {
...(config.theme?.extend || {}),
colors: {
...(config.theme?.extend?.colors || {}),
...materialColors,
},
},
}
: {
...(config.theme || {}),
colors: { ...(config.theme?.colors || {}), ...materialColors },
},
plugins: [
...(config.plugins || []),
...tailwindMaterialSurfaces({
surfacePrefix: "bg",
interactiveSurfacePrefix: "interactive-bg",
draggedSurfacePrefix: "dragged-bg",
disabledStyles: {
textOpacity: 0.38,
backgroundOpacity: 0.12,
colorName: "on-surface",
},
}),
],
});
} else {
throw "A primary color must be specified";
}
};
@@ -0,0 +1,103 @@
const colorMix = require("tailwindcss-color-mix");
const plugin = require("tailwindcss/plugin");
const {
default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette");
const DEFAULT_OPTIONS = {
hoverAmount: "8",
pressAmount: "12",
focusAmount: "12",
dragAmount: "16",
surfacePrefix: "surface",
interactiveSurfacePrefix: "interactive-surface",
draggedSurfacePrefix: "dragged-surface",
disabledStyles: {
textOpacity: 0.38,
backgroundOpacity: 0.12,
colorName: "black",
},
transition: {
duration: 150,
},
};
module.exports = (options = {}) => {
const {
surfacePrefix,
interactiveSurfacePrefix,
draggedSurfacePrefix,
disabledStyles,
transition,
...mixAmounts
} = {
...DEFAULT_OPTIONS,
...options,
};
return [
colorMix(),
plugin(({ matchComponents, theme }) => {
const colors = flattenColorPalette(theme("colors"));
const materialColors = Object.keys(colors).filter(
(colorName) => colors[`on-${colorName}`]
);
const materialColorsObject = Object.fromEntries(
materialColors.map((colorName) => [colorName, colorName])
);
matchComponents(
{
[surfacePrefix]: (colorName) => ({
...(surfacePrefix === "bg"
? {}
: {
[`@apply bg-${colorName}`]: {},
}),
[`@apply text-on-${colorName}`]: {},
}),
[interactiveSurfacePrefix]: (colorName) => ({
[`@apply bg-${colorName}`]: {},
[`@apply text-on-${colorName}`]: {},
[`@apply bg-mix-on-${colorName}`]: {},
...(transition
? {
[`@apply transition-colors`]: {},
[`@apply duration-${transition.duration}`]: {},
}
: {}),
[`@apply hover:bg-mix-amount-[${mixAmounts.hoverAmount}]`]: {},
[`@apply active:bg-mix-amount-[${mixAmounts.pressAmount}]`]: {},
[`@apply focus-visible:bg-mix-amount-[${mixAmounts.focusAmount}]`]:
{},
...(disabledStyles
? {
[`@apply disabled:text-${disabledStyles.colorName}/[${disabledStyles.textOpacity}]`]:
{},
[`@apply disabled:bg-${disabledStyles.colorName}/[${disabledStyles.backgroundOpacity}]`]:
{},
}
: {}),
}),
[draggedSurfacePrefix]: (colorName) => ({
[`@apply bg-${colorName}`]: {},
[`@apply text-on-${colorName}`]: {},
[`@apply bg-mix-on-${colorName}`]: {},
[`@apply bg-mix-amount-[${mixAmounts.dragAmount}]`]: {},
...(transition
? {
[`@apply transition-colors`]: {},
[`@apply duration-${transition.duration}`]: {},
}
: {}),
}),
},
{
values: materialColorsObject,
}
);
}, {}),
];
};
@@ -0,0 +1,237 @@
const plugin = require("tailwindcss/plugin");
const Color = require("color");
// from tailwindcss src/util/flattenColorPalette
const flattenColorPalette = (colors) =>
Object.assign(
{},
...Object.entries(colors ?? {}).flatMap(([color, values]) =>
typeof values == "object"
? Object.entries(flattenColorPalette(values)).map(([number, hex]) => ({
[color + (number === "DEFAULT" ? "" : `-${number}`)]: hex,
}))
: [{ [`${color}`]: values }]
)
);
const processColors = (
palette,
styles,
{ usesMediaStrategy, darkSelector, lightId, darkId, variablePrefix = "" }
) => {
if (!palette) return { colors: {}, styles };
const colors = flattenColorPalette(palette);
Object.keys(colors).forEach((colorName) => {
const match = colorName.match(
new RegExp(`^(?:(.+)-)?${lightId}(?:-(.+))?$`)
);
if (match) {
const prefix = match[1];
const suffix = match[2];
const modeAwareColorName = [prefix, suffix].filter((x) => x).join("-");
const lightColor = colors[colorName];
const darkColor =
colors[[prefix, darkId, suffix].filter((x) => x).join("-")];
if (lightColor && darkColor) {
if (colors[modeAwareColorName]) {
throw `withModeAwareColors plugin error: adding the '${modeAwareColorName}' mode-aware color would overwrite an existing color.`;
} else {
const lightArray = Color(lightColor).rgb().array();
const lightColorStyle = lightArray.slice(0, 3).join(" ");
const lightOpacityStyle =
lightArray.length > 3 ? `${lightArray[3] * 100}%` : undefined;
const darkArray = Color(darkColor).rgb().array();
const darkColorStyle = darkArray.slice(0, 3).join(" ");
const darkOpacityStyle =
darkArray.length > 3 ? `${darkArray[3] * 100}%` : undefined;
const colorVariable = `--color-${
variablePrefix ? `${variablePrefix}-` : ""
}${modeAwareColorName}`;
const opacityVariable = `--opacity-${
variablePrefix ? `${variablePrefix}-` : ""
}${modeAwareColorName}`;
colors[modeAwareColorName] = ({ opacityValue }) =>
`rgb(var(${colorVariable}) / ${
opacityValue
? typeof opacityValue === "string" &&
opacityValue.startsWith("var(")
? `var(${opacityVariable}, ${opacityValue})`
: opacityValue
: `var(${opacityVariable}, 1)`
})`;
styles[":root"][colorVariable] = lightColorStyle;
if (lightOpacityStyle) {
styles[":root"][opacityVariable] = lightOpacityStyle;
}
if (usesMediaStrategy) {
styles["@media (prefers-color-scheme: dark)"][":root"][
colorVariable
] = darkColorStyle;
if (darkOpacityStyle) {
styles["@media (prefers-color-scheme: dark)"][":root"][
opacityVariable
] = darkOpacityStyle;
}
} else {
styles[darkSelector][colorVariable] = darkColorStyle;
if (darkOpacityStyle) {
styles[darkSelector][opacityVariable] = darkOpacityStyle;
}
}
}
}
}
});
return { colors, styles };
};
module.exports = (
config,
{ lightId, darkId } = { lightId: "light", darkId: "dark" }
) => {
const usesMediaStrategy = Array.isArray(config.darkMode)
? config.darkMode[0] !== "selector"
: config.darkMode !== "selector";
const darkSelector =
!usesMediaStrategy &&
(Array.isArray(config.darkMode) ? config.darkMode[1] || ".dark" : ".dark");
const styles = {
":root": {},
...(usesMediaStrategy
? { "@media (prefers-color-scheme: dark)": { ":root": {} } }
: { [darkSelector]: {} }),
};
// color
if (config.theme?.colors) {
const { colors } = processColors(config.theme.colors, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
});
config.theme.colors = colors;
}
if (config.theme?.extend?.colors) {
const { colors } = processColors(config.theme.extend.colors, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
});
config.theme.extend.colors = colors;
}
// textColor
if (config.theme?.textColor) {
const { colors } = processColors(config.theme.textColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "text",
});
config.theme.textColor = colors;
}
if (config.theme?.extend?.textColor) {
const { colors } = processColors(config.theme.extend.textColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "text",
});
config.theme.extend.textColor = colors;
}
// backgroundColor
if (config.theme?.backgroundColor) {
const { colors } = processColors(config.theme.backgroundColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "background",
});
config.theme.backgroundColor = colors;
}
if (config.theme?.extend?.backgroundColor) {
const { colors } = processColors(
config.theme.extend.backgroundColor,
styles,
{
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "background",
}
);
config.theme.extend.backgroundColor = colors;
}
// borderColor
if (config.theme?.borderColor) {
const { colors } = processColors(config.theme.borderColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "border",
});
config.theme.borderColor = colors;
}
if (config.theme?.extend?.borderColor) {
const { colors } = processColors(config.theme.extend.borderColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "border",
});
config.theme.extend.borderColor = colors;
}
// outlineColor
if (config.theme?.outlineColor) {
const { colors } = processColors(config.theme.outlineColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "outline",
});
config.theme.outlineColor = colors;
}
if (config.theme?.extend?.outlineColor) {
const { colors } = processColors(config.theme.extend.outlineColor, styles, {
usesMediaStrategy,
darkSelector,
lightId,
darkId,
variablePrefix: "outline",
});
config.theme.extend.outlineColor = colors;
}
return {
...config,
plugins: [
...(config.plugins || []),
plugin(({ addBase }) => addBase(styles)),
],
};
};
@@ -0,0 +1,145 @@
import {
argbFromHex,
hexFromArgb,
customColor,
SchemeContent,
Hct,
MaterialDynamicColors,
SchemeExpressive,
SchemeFidelity,
SchemeMonochrome,
SchemeNeutral,
SchemeTonalSpot,
SchemeVibrant,
} from "@material/material-color-utilities";
import { kebabize } from "./util/kebabize";
const AllMaterialDynamicColors = {
background: MaterialDynamicColors.background,
"on-background": MaterialDynamicColors.onBackground,
surface: MaterialDynamicColors.surface,
"surface-dim": MaterialDynamicColors.surfaceDim,
"surface-bright": MaterialDynamicColors.surfaceBright,
"surface-container-lowest": MaterialDynamicColors.surfaceContainerLowest,
"surface-container-low": MaterialDynamicColors.surfaceContainerLow,
"surface-container": MaterialDynamicColors.surfaceContainer,
"surface-container-high": MaterialDynamicColors.surfaceContainerHigh,
"surface-container-highest": MaterialDynamicColors.surfaceContainerHighest,
"on-surface": MaterialDynamicColors.onSurface,
"on-surface-dim": MaterialDynamicColors.onSurface,
"on-surface-bright": MaterialDynamicColors.onSurface,
"on-surface-container-lowest": MaterialDynamicColors.onSurface,
"on-surface-container-low": MaterialDynamicColors.onSurface,
"on-surface-container": MaterialDynamicColors.onSurface,
"on-surface-container-high": MaterialDynamicColors.onSurface,
"on-surface-container-highest": MaterialDynamicColors.onSurface,
"surface-variant": MaterialDynamicColors.surfaceVariant,
"on-surface-variant": MaterialDynamicColors.onSurfaceVariant,
"inverse-surface": MaterialDynamicColors.inverseSurface,
"on-inverse-surface": MaterialDynamicColors.inverseOnSurface,
outline: MaterialDynamicColors.outline,
"outline-variant": MaterialDynamicColors.outlineVariant,
"surface-tint": MaterialDynamicColors.surfaceTint,
"on-surface-tint": MaterialDynamicColors.onSurface,
primary: MaterialDynamicColors.primary,
"on-primary": MaterialDynamicColors.onPrimary,
"primary-container": MaterialDynamicColors.primaryContainer,
"on-primary-container": MaterialDynamicColors.onPrimaryContainer,
"inverse-primary": MaterialDynamicColors.inversePrimary,
secondary: MaterialDynamicColors.secondary,
"on-secondary": MaterialDynamicColors.onSecondary,
"secondary-container": MaterialDynamicColors.secondaryContainer,
"on-secondary-container": MaterialDynamicColors.onSecondaryContainer,
tertiary: MaterialDynamicColors.tertiary,
"on-tertiary": MaterialDynamicColors.onTertiary,
"tertiary-container": MaterialDynamicColors.tertiaryContainer,
"on-tertiary-container": MaterialDynamicColors.onTertiaryContainer,
error: MaterialDynamicColors.error,
"on-error": MaterialDynamicColors.onError,
"error-container": MaterialDynamicColors.errorContainer,
"on-error-container": MaterialDynamicColors.onErrorContainer,
scrim: MaterialDynamicColors.scrim,
shadow: MaterialDynamicColors.shadow,
};
/**
*
* @param {Record<string, string>} colorsMap Values must be hex colors and 'primary' must be present.
*/
export const tailwindThemeFromColor = (colorsMap, scheme, contrast) => {
const { primary, ...extraColors } = colorsMap;
const source = argbFromHex(primary);
const SchemeObjects = {
content: SchemeContent,
expressive: SchemeExpressive,
fidelity: SchemeFidelity,
monochrome: SchemeMonochrome,
neutral: SchemeNeutral,
tonalSpot: SchemeTonalSpot,
vibrant: SchemeVibrant,
};
const SchemeObject = SchemeObjects[scheme] || SchemeObjects.content;
const lightScheme = new SchemeObject(Hct.fromInt(source), false, contrast);
const darkScheme = new SchemeObject(Hct.fromInt(source), true, contrast);
// default colors
const colors = {
transparent: "transparent",
current: "currentColor",
black: "#000000",
white: "#ffffff",
};
Object.entries(AllMaterialDynamicColors).forEach(([name, DynamicColor]) => {
const lightHex = hexFromArgb(DynamicColor.getArgb(lightScheme));
const darkHex = hexFromArgb(DynamicColor.getArgb(darkScheme));
colors[`${name}-light`] = lightHex;
colors[`${name}-dark`] = darkHex;
});
Object.keys(extraColors).forEach((colorName) => {
const value = extraColors[colorName];
const hex = typeof value === "string" ? value : value.hex;
const blend = value === hex ? true : value.harmonize;
const { light, dark } = customColor(source, {
value: argbFromHex(hex),
blend,
});
const kebabName = kebabize(colorName);
colors[`${kebabName}-light`] = hexFromArgb(light.color);
colors[`on-${kebabName}-light`] = hexFromArgb(light.onColor);
colors[`${kebabName}-container-light`] = hexFromArgb(light.colorContainer);
colors[`on-${kebabName}-container-light`] = hexFromArgb(
light.onColorContainer
);
colors[`${kebabName}-dark`] = hexFromArgb(dark.color);
colors[`on-${kebabName}-dark`] = hexFromArgb(dark.onColor);
colors[`${kebabName}-container-dark`] = hexFromArgb(dark.colorContainer);
colors[`on-${kebabName}-container-dark`] = hexFromArgb(
dark.onColorContainer
);
});
return colors;
};
@@ -0,0 +1,10 @@
export const kebabize = (str) => {
return str
.split("")
.map((letter, idx) => {
return letter.toUpperCase() === letter
? `${idx !== 0 ? "-" : ""}${letter.toLowerCase()}`
: letter;
})
.join("");
};