From b22eff9a90e7c51b8e2d2f974c8bb83239cb7912 Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Mon, 13 Oct 2025 22:51:07 +0200 Subject: [PATCH 1/8] Add theme config property --- .../src/airflow/api_fastapi/common/types.py | 15 ++++++ .../core_api/datamodels/ui/config.py | 3 +- .../core_api/openapi/_private_ui.yaml | 40 ++++++++++++++ .../api_fastapi/core_api/routes/ui/config.py | 23 ++++++++ .../src/airflow/config_templates/config.yml | 52 +++++++++++++++++++ .../ui/openapi-gen/requests/schemas.gen.ts | 42 ++++++++++++++- .../ui/openapi-gen/requests/types.gen.ts | 16 ++++++ .../core_api/routes/ui/test_config.py | 24 +++++++++ 8 files changed, 213 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/types.py b/airflow-core/src/airflow/api_fastapi/common/types.py index c5df6259f4d5b..9dbf81fd43461 100644 --- a/airflow-core/src/airflow/api_fastapi/common/types.py +++ b/airflow-core/src/airflow/api_fastapi/common/types.py @@ -107,3 +107,18 @@ class UIAlert(BaseModel): text: str category: Literal["info", "warning", "error"] + + +class Theme(BaseModel): + """JSON to modify Chakra's theme.""" + + tokens: dict[ + Literal["colors"], + dict[ + Literal["brand"], + dict[ + Literal["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"], + dict[Literal["value"], str], + ], + ], + ] diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py index 5644f49e0cfa2..59935882e5e85 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py @@ -18,7 +18,7 @@ from pydantic import BaseModel -from airflow.api_fastapi.common.types import UIAlert +from airflow.api_fastapi.common.types import Theme, UIAlert class ConfigResponse(BaseModel): @@ -35,3 +35,4 @@ class ConfigResponse(BaseModel): dashboard_alert: list[UIAlert] show_external_log_redirect: bool external_log_name: str | None = None + theme: Theme diff --git a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml index ac7f962d3c150..858d5f8aeeba1 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml +++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml @@ -1329,6 +1329,8 @@ components: - type: string - type: 'null' title: External Log Name + theme: + $ref: '#/components/schemas/Theme' type: object required: - page_size @@ -1341,6 +1343,7 @@ components: - test_connection - dashboard_alert - show_external_log_redirect + - theme title: ConfigResponse description: configuration serializer. ConnectionHookFieldBehavior: @@ -2650,6 +2653,43 @@ components: - name title: TeamResponse description: Base serializer for Team. + Theme: + properties: + tokens: + additionalProperties: + additionalProperties: + additionalProperties: + additionalProperties: + type: string + propertyNames: + const: value + type: object + propertyNames: + enum: + - '50' + - '100' + - '200' + - '300' + - '400' + - '500' + - '600' + - '700' + - '800' + - '900' + - '950' + type: object + propertyNames: + const: brand + type: object + propertyNames: + const: colors + type: object + title: Tokens + type: object + required: + - tokens + title: Theme + description: JSON to modify Chakra's theme. TriggerResponse: properties: id: diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py index c0b7dd2e3b638..7ff796d69c230 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py @@ -16,6 +16,7 @@ # under the License. from __future__ import annotations +from json import loads from typing import Any from fastapi import Depends, status @@ -31,6 +32,27 @@ config_router = AirflowRouter(tags=["Config"]) +THEME_FALLBACK = """ +{ + "tokens": { + "colors": { + "brand": { + "50": { "value": "oklch(0.971 0.013 17.38)" }, + "100": { "value": "oklch(0.936 0.032 17.717)" }, + "200": { "value": "oklch(0.885 0.062 18.334)" }, + "300": { "value": "oklch(0.808 0.114 19.571)" }, + "400": { "value": "oklch(0.704 0.191 22.216)" }, + "500": { "value": "oklch(0.637 0.237 25.331)" }, + "600": { "value": "oklch(0.577 0.245 27.325)" }, + "700": { "value": "oklch(0.505 0.213 27.518)" }, + "800": { "value": "oklch(0.444 0.177 26.899)" }, + "900": { "value": "oklch(0.396 0.141 25.723)" }, + "950": { "value": "oklch(0.258 0.092 26.042)" } + } + } + } +} +""" API_CONFIG_KEYS = [ "enable_swagger_ui", @@ -59,6 +81,7 @@ def get_configs() -> ConfigResponse: "dashboard_alert": [alert for alert in DASHBOARD_UIALERTS if isinstance(alert, UIAlert)], "show_external_log_redirect": task_log_reader.supports_external_link, "external_log_name": getattr(task_log_reader.log_handler, "log_name", None), + "theme": loads(conf.get("api", "theme", fallback=THEME_FALLBACK)), } config.update({key: value for key, value in additional_config.items()}) diff --git a/airflow-core/src/airflow/config_templates/config.yml b/airflow-core/src/airflow/config_templates/config.yml index 285f02c84a604..ef4291e06a956 100644 --- a/airflow-core/src/airflow/config_templates/config.yml +++ b/airflow-core/src/airflow/config_templates/config.yml @@ -1359,6 +1359,58 @@ api: type: string example: ~ default: + theme: + description: | + JSON config to customiza the Chakra UI theme. + Currently only supports ``brand`` color customization. + + Must supply ``50``-``950`` color values for ``brand`` color. + + Note: As shown below, you can split your json config over multiple lines by indenting. + See configparser documentation for an example: + https://docs.python.org/3/library/configparser.html#supported-ini-file-structure. + version_added: ~ + type: string + example: > + { + "tokens": { + "colors": { + "brand": { + "50": { "value": "oklch(0.971 0.013 17.38)" }, + "100": { "value": "oklch(0.936 0.032 17.717)" }, + "200": { "value": "oklch(0.885 0.062 18.334)" }, + "300": { "value": "oklch(0.808 0.114 19.571)" }, + "400": { "value": "oklch(0.704 0.191 22.216)" }, + "500": { "value": "oklch(0.637 0.237 25.331)" }, + "600": { "value": "oklch(0.577 0.245 27.325)" }, + "700": { "value": "oklch(0.505 0.213 27.518)" }, + "800": { "value": "oklch(0.444 0.177 26.899)" }, + "900": { "value": "oklch(0.396 0.141 25.723)" }, + "950": { "value": "oklch(0.258 0.092 26.042)" } + } + } + } + } + default: > + {{ + "tokens": {{ + "colors": {{ + "brand": {{ + "50": {{ "value": "oklch(0.98 0.006 248.717)" }}, + "100": {{ "value": "oklch(0.962 0.012 249.460)" }}, + "200": {{ "value": "oklch(0.923 0.023 255.082)" }}, + "300": {{ "value": "oklch(0.865 0.039 252.420)" }}, + "400": {{ "value": "oklch(0.705 0.066 256.378)" }}, + "500": {{ "value": "oklch(0.575 0.08 257.759)" }}, + "600": {{ "value": "oklch(0.469 0.084 257.657)" }}, + "700": {{ "value": "oklch(0.399 0.084 257.850)" }}, + "800": {{ "value": "oklch(0.324 0.072 260.329)" }}, + "900": {{ "value": "oklch(0.259 0.062 265.566)" }}, + "950": {{ "value": "oklch(0.179 0.05 265.487)" }} + }} + }} + }} + }} enable_swagger_ui: description: | Boolean for running SwaggerUI in the webserver. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts index 5a5f9e3d220ae..45bb74e314751 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts @@ -7102,10 +7102,13 @@ export const $ConfigResponse = { } ], title: 'External Log Name' + }, + theme: { + '$ref': '#/components/schemas/Theme' } }, type: 'object', - required: ['page_size', 'auto_refresh_interval', 'hide_paused_dags_by_default', 'instance_name', 'enable_swagger_ui', 'require_confirmation_dag_change', 'default_wrap', 'test_connection', 'dashboard_alert', 'show_external_log_redirect'], + required: ['page_size', 'auto_refresh_interval', 'hide_paused_dags_by_default', 'instance_name', 'enable_swagger_ui', 'require_confirmation_dag_change', 'default_wrap', 'test_connection', 'dashboard_alert', 'show_external_log_redirect', 'theme'], title: 'ConfigResponse', description: 'configuration serializer.' } as const; @@ -8254,6 +8257,43 @@ export const $TeamResponse = { description: 'Base serializer for Team.' } as const; +export const $Theme = { + properties: { + tokens: { + additionalProperties: { + additionalProperties: { + additionalProperties: { + additionalProperties: { + type: 'string' + }, + propertyNames: { + const: 'value' + }, + type: 'object' + }, + propertyNames: { + enum: ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950'] + }, + type: 'object' + }, + propertyNames: { + const: 'brand' + }, + type: 'object' + }, + propertyNames: { + const: 'colors' + }, + type: 'object', + title: 'Tokens' + } + }, + type: 'object', + required: ['tokens'], + title: 'Theme', + description: "JSON to modify Chakra's theme." +} as const; + export const $UIAlert = { properties: { text: { diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts index 18a3056024d79..2e6cfa365980b 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts @@ -1767,6 +1767,7 @@ export type ConfigResponse = { dashboard_alert: Array; show_external_log_redirect: boolean; external_log_name?: string | null; + theme: Theme; }; /** @@ -2053,6 +2054,21 @@ export type TeamResponse = { name: string; }; +/** + * JSON to modify Chakra's theme. + */ +export type Theme = { + tokens: { + [key: string]: { + [key: string]: { + [key: string]: { + [key: string]: (string); + }; + }; + }; + }; +}; + /** * Optional alert to be shown at the top of the page. */ diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py index 56d37c9f8915d..bca3a28614d7f 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py @@ -23,6 +23,28 @@ pytestmark = pytest.mark.db_test +THEME = """ +{ + "tokens": { + "colors": { + "brand": { + "50": { "value": "oklch(0.971 0.013 17.38)" }, + "100": { "value": "oklch(0.936 0.032 17.717)" }, + "200": { "value": "oklch(0.885 0.062 18.334)" }, + "300": { "value": "oklch(0.808 0.114 19.571)" }, + "400": { "value": "oklch(0.704 0.191 22.216)" }, + "500": { "value": "oklch(0.637 0.237 25.331)" }, + "600": { "value": "oklch(0.577 0.245 27.325)" }, + "700": { "value": "oklch(0.505 0.213 27.518)" }, + "800": { "value": "oklch(0.444 0.177 26.899)" }, + "900": { "value": "oklch(0.396 0.141 25.723)" }, + "950": { "value": "oklch(0.258 0.092 26.042)" } + } + } + } +} +""" + mock_config_response = { "page_size": 100, "auto_refresh_interval": 3, @@ -35,6 +57,7 @@ "dashboard_alert": [], "show_external_log_redirect": False, "external_log_name": None, + "theme": THEME, } @@ -52,6 +75,7 @@ def mock_config_data(): ("api", "default_wrap"): "false", ("api", "auto_refresh_interval"): "3", ("api", "require_confirmation_dag_change"): "false", + ("api", "theme"): THEME, } ): yield From 6cc310c867d3f690b82a30297425403b73ead963 Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Sun, 16 Nov 2025 00:03:25 +0100 Subject: [PATCH 2/8] Add custom theme to Chakra UI's theme --- .../chakraCustom/ChakraCustomProvider.tsx | 38 + .../ui/src/context/chakraCustom/index.ts | 20 + airflow-core/src/airflow/ui/src/main.tsx | 15 +- .../pages/TaskInstance/Logs/TaskLogHeader.tsx | 3 +- airflow-core/src/airflow/ui/src/theme.ts | 722 +++++++++--------- 5 files changed, 431 insertions(+), 367 deletions(-) create mode 100644 airflow-core/src/airflow/ui/src/context/chakraCustom/ChakraCustomProvider.tsx create mode 100644 airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts diff --git a/airflow-core/src/airflow/ui/src/context/chakraCustom/ChakraCustomProvider.tsx b/airflow-core/src/airflow/ui/src/context/chakraCustom/ChakraCustomProvider.tsx new file mode 100644 index 0000000000000..1c1c7b88a6391 --- /dev/null +++ b/airflow-core/src/airflow/ui/src/context/chakraCustom/ChakraCustomProvider.tsx @@ -0,0 +1,38 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { ChakraProvider } from "@chakra-ui/react"; +import { useMemo, type PropsWithChildren } from "react"; + +import type { Theme } from "openapi/requests/types.gen"; +import { useConfig } from "src/queries/useConfig"; +import { createTheme } from "src/theme"; + +export const ChakraCustomProvider = ({ children }: PropsWithChildren) => { + const theme = useConfig("theme"); + + const system = useMemo(() => { + if (typeof theme === "undefined") { + return undefined; + } + + return createTheme(theme as Theme); + }, [theme]); + + return system && {children}; +}; diff --git a/airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts b/airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts new file mode 100644 index 0000000000000..0ad3c5ef2293b --- /dev/null +++ b/airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts @@ -0,0 +1,20 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from "./ChakraCustomProvider"; diff --git a/airflow-core/src/airflow/ui/src/main.tsx b/airflow-core/src/airflow/ui/src/main.tsx index a98de89b4868e..6ed6f3dba7613 100644 --- a/airflow-core/src/airflow/ui/src/main.tsx +++ b/airflow-core/src/airflow/ui/src/main.tsx @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { ChakraProvider } from "@chakra-ui/react"; import { QueryClientProvider } from "@tanstack/react-query"; import axios, { type AxiosError } from "axios"; import { StrictMode } from "react"; @@ -29,6 +28,7 @@ import * as ReactRouterDOM from "react-router-dom"; import * as ReactJSXRuntime from "react/jsx-runtime"; import type { HTTPExceptionResponse } from "openapi/requests/types.gen"; +import { ChakraCustomProvider } from "src/context/chakraCustom"; import { ColorModeProvider } from "src/context/colorMode"; import { TimezoneProvider } from "src/context/timezone"; import { router } from "src/router"; @@ -36,7 +36,6 @@ import { getRedirectPath } from "src/utils/links.ts"; import i18n from "./i18n/config"; import { client } from "./queryClient"; -import { system } from "./theme"; // Set React, ReactDOM, and ReactJSXRuntime on globalThis to share them with the dynamically imported React plugins. // Only one instance of React should be used. @@ -69,15 +68,15 @@ axios.interceptors.response.use( createRoot(document.querySelector("#root") as HTMLDivElement).render( - - - + + + - - - + + + , ); diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx index 0d8d6c88e7722..bf925e7fcdee6 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx @@ -42,7 +42,7 @@ import type { TaskInstanceResponse } from "openapi/requests/types.gen"; import { TaskTrySelect } from "src/components/TaskTrySelect"; import { Button, Menu, Select, Tooltip } from "src/components/ui"; import { SearchParamsKeys } from "src/constants/searchParams"; -import { system } from "src/theme"; +import { createTheme } from "src/theme"; import { type LogLevel, logLevelColorMapping, logLevelOptions } from "src/utils/logs"; type Props = { @@ -85,6 +85,7 @@ export const TaskLogHeader = ({ const sources = searchParams.getAll(SearchParamsKeys.SOURCE); const logLevels = searchParams.getAll(SearchParamsKeys.LOG_LEVEL); const hasLogLevels = logLevels.length > 0; + const system = createTheme(); // Have select zIndex greater than modal zIndex in fullscreen so that // select options are displayed. diff --git a/airflow-core/src/airflow/ui/src/theme.ts b/airflow-core/src/airflow/ui/src/theme.ts index bd8206c11d338..5bf6a0f7e8e0c 100644 --- a/airflow-core/src/airflow/ui/src/theme.ts +++ b/airflow-core/src/airflow/ui/src/theme.ts @@ -23,6 +23,8 @@ import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"; import type { CSSProperties } from "react"; +import type { Theme } from "openapi/requests/types.gen"; + const generateSemanticTokens = (color: string, darkContrast: string = "white") => ({ solid: { value: `{colors.${color}.600}` }, contrast: { value: { _light: "white", _dark: darkContrast } }, @@ -33,371 +35,375 @@ const generateSemanticTokens = (color: string, darkContrast: string = "white") = focusRing: { value: { _light: `{colors.${color}.800}`, _dark: `{colors.${color}.200}` } }, }); -export const customConfig = defineConfig({ +const defaultTheme = { // See https://chakra-ui.com/docs/theming/colors for more information on the colors used here. - theme: { - tokens: { - colors: { - black: { value: "oklch(0.23185 0.0323 266.44)" }, // Custom value for dark mode - brand: { - "50": { value: "oklch(0.98 0.006 248.717)" }, - "100": { value: "oklch(0.962 0.012 249.460)" }, - "200": { value: "oklch(0.923 0.023 255.082)" }, - "300": { value: "oklch(0.865 0.039 252.420)" }, - "400": { value: "oklch(0.705 0.066 256.378)" }, - "500": { value: "oklch(0.575 0.08 257.759)" }, - "600": { value: "oklch(0.469 0.084 257.657)" }, - "700": { value: "oklch(0.399 0.084 257.850)" }, - "800": { value: "oklch(0.324 0.072 260.329)" }, - "900": { value: "oklch(0.259 0.062 265.566)" }, - "950": { value: "oklch(0.179 0.05 265.487)" }, - }, - gray: { - // Values modified from original Tailwind to improve contrast in Chakra UI - "50": { value: "oklch(0.985 0.004 253)" }, // Original: oklch(0.985 0.002 247.839) - "100": { value: "oklch(0.955 0.006 253)" }, // Original: oklch(0.967 0.003 264.542) - "200": { value: "oklch(0.915 0.01 253)" }, // Original: oklch(0.928 0.006 264.531) - "300": { value: "oklch(0.85 0.016 253)" }, // Original: oklch(0.872 0.01 258.338) - "400": { value: "oklch(0.75 0.025 252)" }, // Original: oklch(0.707 0.022 261.325) - "500": { value: "oklch(0.63 0.042 252)" }, // Original: oklch(0.551 0.027 264.364) - "600": { value: "oklch(0.45 0.055 251)" }, // Original: oklch(0.446 0.03 256.802) - "700": { value: "oklch(0.35 0.045 251)" }, // Original: oklch(0.373 0.034 259.733) - "800": { value: "oklch(0.28 0.035 251)" }, // Original: oklch(0.278 0.033 256.848) - "900": { value: "oklch(0.18 0.03 251)" }, // Original: oklch(0.21 0.034 264.665) - "950": { value: "oklch(0.11 0.025 251)" }, // Original: oklch(0.13 0.028 261.692) - }, - // TAILWIND 4.0 COLORS - // See https://tailwindcss.com/docs/colors for more information on the colors used here. - red: { - "50": { value: "oklch(0.971 0.013 17.38)" }, - "100": { value: "oklch(0.936 0.032 17.717)" }, - "200": { value: "oklch(0.885 0.062 18.334)" }, - "300": { value: "oklch(0.808 0.114 19.571)" }, - "400": { value: "oklch(0.704 0.191 22.216)" }, - "500": { value: "oklch(0.637 0.237 25.331)" }, - "600": { value: "oklch(0.577 0.245 27.325)" }, - "700": { value: "oklch(0.505 0.213 27.518)" }, - "800": { value: "oklch(0.444 0.177 26.899)" }, - "900": { value: "oklch(0.396 0.141 25.723)" }, - "950": { value: "oklch(0.258 0.092 26.042)" }, - }, + tokens: { + colors: { + black: { value: "oklch(0.23185 0.0323 266.44)" }, // Custom value for dark mode + brand: { + "50": { value: "oklch(0.98 0.006 248.717)" }, + "100": { value: "oklch(0.962 0.012 249.460)" }, + "200": { value: "oklch(0.923 0.023 255.082)" }, + "300": { value: "oklch(0.865 0.039 252.420)" }, + "400": { value: "oklch(0.705 0.066 256.378)" }, + "500": { value: "oklch(0.575 0.08 257.759)" }, + "600": { value: "oklch(0.469 0.084 257.657)" }, + "700": { value: "oklch(0.399 0.084 257.850)" }, + "800": { value: "oklch(0.324 0.072 260.329)" }, + "900": { value: "oklch(0.259 0.062 265.566)" }, + "950": { value: "oklch(0.179 0.05 265.487)" }, + }, + gray: { // Values modified from original Tailwind to improve contrast in Chakra UI - orange: { - "50": { value: "oklch(0.982 0.013 83.915)" }, - "100": { value: "oklch(0.961 0.033 82.320)" }, - "200": { value: "oklch(0.918 0.065 79.975)" }, - "300": { value: "oklch(0.857 0.118 76.815)" }, - "400": { value: "oklch(0.7492 0.1439 62.081)" }, // Original: oklch(0.774 0.186 71.555) - "500": { value: "oklch(0.6462 0.1979 43.792)" }, // Original: oklch(0.705 0.213 47.604) - "600": { value: "oklch(0.5902 0.198 35.93)" }, // Original: oklch(0.632 0.214 41.185) - "700": { value: "oklch(0.553 0.184 41.777)" }, - "800": { value: "oklch(0.469 0.144 45.164)" }, - "900": { value: "oklch(0.414 0.110 48.717)" }, - "950": { value: "oklch(0.271 0.069 52.345)" }, - }, - amber: { - "50": { value: "oklch(0.987 0.022 95.277)" }, - "100": { value: "oklch(0.962 0.059 95.617)" }, - "200": { value: "oklch(0.924 0.12 95.746)" }, - "300": { value: "oklch(0.879 0.169 91.605)" }, - "400": { value: "oklch(0.828 0.189 84.429)" }, - "500": { value: "oklch(0.769 0.188 70.08)" }, - "600": { value: "oklch(0.666 0.179 58.318)" }, - "700": { value: "oklch(0.555 0.163 48.998)" }, - "800": { value: "oklch(0.473 0.137 46.201)" }, - "900": { value: "oklch(0.414 0.112 45.904)" }, - "950": { value: "oklch(0.279 0.077 45.635)" }, - }, - yellow: { - "50": { value: "oklch(0.987 0.026 102.212)" }, - "100": { value: "oklch(0.973 0.071 103.193)" }, - "200": { value: "oklch(0.945 0.129 101.54)" }, - "300": { value: "oklch(0.905 0.182 98.111)" }, - "400": { value: "oklch(0.852 0.199 91.936)" }, - "500": { value: "oklch(0.795 0.184 86.047)" }, - "600": { value: "oklch(0.681 0.162 75.834)" }, - "700": { value: "oklch(0.554 0.135 66.442)" }, - "800": { value: "oklch(0.476 0.114 61.907)" }, - "900": { value: "oklch(0.421 0.095 57.708)" }, - "950": { value: "oklch(0.286 0.066 53.813)" }, - }, - lime: { - "50": { value: "oklch(0.986 0.031 120.757)" }, - "100": { value: "oklch(0.967 0.067 122.328)" }, - "200": { value: "oklch(0.938 0.127 124.321)" }, - "300": { value: "oklch(0.897 0.196 126.665)" }, - "400": { value: "oklch(0.841 0.238 128.85)" }, - "500": { value: "oklch(0.768 0.233 130.85)" }, - "600": { value: "oklch(0.648 0.2 131.684)" }, - "700": { value: "oklch(0.532 0.157 131.589)" }, - "800": { value: "oklch(0.453 0.124 130.933)" }, - "900": { value: "oklch(0.405 0.101 131.063)" }, - "950": { value: "oklch(0.274 0.072 132.109)" }, - }, - green: { - // Values modified from original Tailwind to improve contrast in Chakra UI - "50": { value: "oklch(0.982 0.018 155.826)" }, - "100": { value: "oklch(0.962 0.044 156.743)" }, - "200": { value: "oklch(0.925 0.084 155.995)" }, - "300": { value: "oklch(0.75 0.18 153.0)" }, // Original: oklch(0.871 0.15 154.449) - "400": { value: "oklch(0.625 0.209 150.0)" }, // Original: oklch(0.792 0.209 151.711) - "500": { value: "oklch(0.528 0.219 149.579)" }, // Original: oklch(0.723 0.219 149.579) - "600": { value: "oklch(0.47 0.20 149.0)" }, // Original: oklch(0.627 0.194 149.214) - "700": { value: "oklch(0.40 0.16 149.5)" }, // Original: oklch(0.527 0.154 150.069) - "800": { value: "oklch(0.448 0.119 151.328)" }, - "900": { value: "oklch(0.393 0.095 152.535)" }, - "950": { value: "oklch(0.266 0.065 152.934)" }, - }, - emerald: { - "50": { value: "oklch(0.979 0.021 166.113)" }, - "100": { value: "oklch(0.95 0.052 163.051)" }, - "200": { value: "oklch(0.905 0.093 164.15)" }, - "300": { value: "oklch(0.845 0.143 164.978)" }, - "400": { value: "oklch(0.765 0.177 163.223)" }, - "500": { value: "oklch(0.696 0.17 162.48)" }, - "600": { value: "oklch(0.596 0.145 163.225)" }, - "700": { value: "oklch(0.508 0.118 165.612)" }, - "800": { value: "oklch(0.432 0.095 166.913)" }, - "900": { value: "oklch(0.378 0.077 168.94)" }, - "950": { value: "oklch(0.262 0.051 172.552)" }, - }, - teal: { - "50": { value: "oklch(0.984 0.014 180.72)" }, - "100": { value: "oklch(0.953 0.051 180.801)" }, - "200": { value: "oklch(0.91 0.096 180.426)" }, - "300": { value: "oklch(0.855 0.138 181.071)" }, - "400": { value: "oklch(0.777 0.152 181.912)" }, - "500": { value: "oklch(0.704 0.14 182.503)" }, - "600": { value: "oklch(0.6 0.118 184.704)" }, - "700": { value: "oklch(0.511 0.096 186.391)" }, - "800": { value: "oklch(0.437 0.078 188.216)" }, - "900": { value: "oklch(0.386 0.063 188.416)" }, - "950": { value: "oklch(0.277 0.046 192.524)" }, - }, - cyan: { - "50": { value: "oklch(0.984 0.019 200.873)" }, - "100": { value: "oklch(0.956 0.045 203.388)" }, - "200": { value: "oklch(0.917 0.08 205.041)" }, - "300": { value: "oklch(0.865 0.127 207.078)" }, - "400": { value: "oklch(0.789 0.154 211.53)" }, - "500": { value: "oklch(0.715 0.143 215.221)" }, - "600": { value: "oklch(0.609 0.126 221.723)" }, - "700": { value: "oklch(0.52 0.105 223.128)" }, - "800": { value: "oklch(0.45 0.085 224.283)" }, - "900": { value: "oklch(0.398 0.07 227.392)" }, - "950": { value: "oklch(0.302 0.056 229.695)" }, - }, - sky: { - "50": { value: "oklch(0.977 0.013 236.62)" }, - "100": { value: "oklch(0.951 0.026 236.824)" }, - "200": { value: "oklch(0.901 0.058 230.902)" }, - "300": { value: "oklch(0.828 0.111 230.318)" }, - "400": { value: "oklch(0.746 0.16 232.661)" }, - "500": { value: "oklch(0.685 0.169 237.323)" }, - "600": { value: "oklch(0.588 0.158 241.966)" }, - "700": { value: "oklch(0.5 0.134 242.749)" }, - "800": { value: "oklch(0.443 0.11 240.79)" }, - "900": { value: "oklch(0.391 0.09 240.876)" }, - "950": { value: "oklch(0.293 0.066 243.157)" }, - }, - blue: { - "50": { value: "oklch(0.97 0.014 254.604)" }, - "100": { value: "oklch(0.932 0.032 255.585)" }, - "200": { value: "oklch(0.882 0.059 254.128)" }, - "300": { value: "oklch(0.809 0.105 251.813)" }, - "400": { value: "oklch(0.707 0.165 254.624)" }, - "500": { value: "oklch(0.623 0.214 259.815)" }, - "600": { value: "oklch(0.546 0.245 262.881)" }, - "700": { value: "oklch(0.488 0.243 264.376)" }, - "800": { value: "oklch(0.424 0.199 265.638)" }, - "900": { value: "oklch(0.379 0.146 265.522)" }, - "950": { value: "oklch(0.282 0.091 267.935)" }, - }, - indigo: { - "50": { value: "oklch(0.962 0.018 272.314)" }, - "100": { value: "oklch(0.93 0.034 272.788)" }, - "200": { value: "oklch(0.87 0.065 274.039)" }, - "300": { value: "oklch(0.785 0.115 274.713)" }, - "400": { value: "oklch(0.673 0.182 276.935)" }, - "500": { value: "oklch(0.585 0.233 277.117)" }, - "600": { value: "oklch(0.511 0.262 276.966)" }, - "700": { value: "oklch(0.457 0.24 277.023)" }, - "800": { value: "oklch(0.398 0.195 277.366)" }, - "900": { value: "oklch(0.359 0.144 278.697)" }, - "950": { value: "oklch(0.257 0.09 281.288)" }, - }, - violet: { - "50": { value: "oklch(0.969 0.016 293.756)" }, - "100": { value: "oklch(0.943 0.029 294.588)" }, - "200": { value: "oklch(0.894 0.057 293.283)" }, - "300": { value: "oklch(0.811 0.111 293.571)" }, - "400": { value: "oklch(0.702 0.183 293.541)" }, - "500": { value: "oklch(0.606 0.25 292.717)" }, - "600": { value: "oklch(0.541 0.281 293.009)" }, - "700": { value: "oklch(0.491 0.27 292.581)" }, - "800": { value: "oklch(0.432 0.232 292.759)" }, - "900": { value: "oklch(0.38 0.189 293.745)" }, - "950": { value: "oklch(0.283 0.141 291.089)" }, - }, - purple: { - "50": { value: "oklch(0.977 0.014 308.299)" }, - "100": { value: "oklch(0.946 0.033 307.174)" }, - "200": { value: "oklch(0.902 0.063 306.703)" }, - "300": { value: "oklch(0.827 0.119 306.383)" }, - "400": { value: "oklch(0.714 0.203 305.504)" }, - "500": { value: "oklch(0.627 0.265 303.9)" }, - "600": { value: "oklch(0.558 0.288 302.321)" }, - "700": { value: "oklch(0.496 0.265 301.924)" }, - "800": { value: "oklch(0.438 0.218 303.724)" }, - "900": { value: "oklch(0.381 0.176 304.987)" }, - "950": { value: "oklch(0.291 0.149 302.717)" }, - }, - fuchsia: { - "50": { value: "oklch(0.977 0.017 320.058)" }, - "100": { value: "oklch(0.952 0.037 318.852)" }, - "200": { value: "oklch(0.903 0.076 319.62)" }, - "300": { value: "oklch(0.833 0.145 321.434)" }, - "400": { value: "oklch(0.74 0.238 322.16)" }, - "500": { value: "oklch(0.667 0.295 322.15)" }, - "600": { value: "oklch(0.591 0.293 322.896)" }, - "700": { value: "oklch(0.518 0.253 323.949)" }, - "800": { value: "oklch(0.452 0.211 324.591)" }, - "900": { value: "oklch(0.401 0.17 325.612)" }, - "950": { value: "oklch(0.293 0.136 325.661)" }, - }, - pink: { - "50": { value: "oklch(0.971 0.014 343.198)" }, - "100": { value: "oklch(0.948 0.028 342.258)" }, - "200": { value: "oklch(0.899 0.061 343.231)" }, - "300": { value: "oklch(0.823 0.12 346.018)" }, - "400": { value: "oklch(0.718 0.202 349.761)" }, - "500": { value: "oklch(0.656 0.241 354.308)" }, - "600": { value: "oklch(0.592 0.249 0.584)" }, - "700": { value: "oklch(0.525 0.223 3.958)" }, - "800": { value: "oklch(0.459 0.187 3.815)" }, - "900": { value: "oklch(0.408 0.153 2.432)" }, - "950": { value: "oklch(0.284 0.109 3.907)" }, - }, - rose: { - "50": { value: "oklch(0.969 0.015 12.422)" }, - "100": { value: "oklch(0.941 0.03 12.58)" }, - "200": { value: "oklch(0.892 0.058 10.001)" }, - "300": { value: "oklch(0.81 0.117 11.638)" }, - "400": { value: "oklch(0.712 0.194 13.428)" }, - "500": { value: "oklch(0.645 0.246 16.439)" }, - "600": { value: "oklch(0.586 0.253 17.585)" }, - "700": { value: "oklch(0.514 0.222 16.935)" }, - "800": { value: "oklch(0.455 0.188 13.697)" }, - "900": { value: "oklch(0.41 0.159 10.272)" }, - "950": { value: "oklch(0.271 0.105 12.094)" }, - }, - slate: { - "50": { value: "oklch(0.984 0.003 247.858)" }, - "100": { value: "oklch(0.968 0.007 247.896)" }, - "200": { value: "oklch(0.929 0.013 255.508)" }, - "300": { value: "oklch(0.869 0.022 252.894)" }, - "400": { value: "oklch(0.704 0.04 256.788)" }, - "500": { value: "oklch(0.554 0.046 257.417)" }, - "600": { value: "oklch(0.446 0.043 257.281)" }, - "700": { value: "oklch(0.372 0.044 257.287)" }, - "800": { value: "oklch(0.279 0.041 260.031)" }, - "900": { value: "oklch(0.208 0.042 265.755)" }, - "950": { value: "oklch(0.129 0.042 264.695)" }, - }, - zinc: { - "50": { value: "oklch(0.985 0 0)" }, - "100": { value: "oklch(0.967 0.001 286.375)" }, - "200": { value: "oklch(0.92 0.004 286.32)" }, - "300": { value: "oklch(0.871 0.006 286.286)" }, - "400": { value: "oklch(0.705 0.015 286.067)" }, - "500": { value: "oklch(0.552 0.016 285.938)" }, - "600": { value: "oklch(0.442 0.017 285.786)" }, - "700": { value: "oklch(0.37 0.013 285.805)" }, - "800": { value: "oklch(0.274 0.006 286.033)" }, - "900": { value: "oklch(0.21 0.006 285.885)" }, - "950": { value: "oklch(0.141 0.005 285.823)" }, - }, - neutral: { - "50": { value: "oklch(0.985 0 0)" }, - "100": { value: "oklch(0.97 0 0)" }, - "200": { value: "oklch(0.922 0 0)" }, - "300": { value: "oklch(0.87 0 0)" }, - "400": { value: "oklch(0.708 0 0)" }, - "500": { value: "oklch(0.556 0 0)" }, - "600": { value: "oklch(0.439 0 0)" }, - "700": { value: "oklch(0.371 0 0)" }, - "800": { value: "oklch(0.269 0 0)" }, - "900": { value: "oklch(0.205 0 0)" }, - "950": { value: "oklch(0.145 0 0)" }, - }, - stone: { - "50": { value: "oklch(0.985 0.001 106.423)" }, - "100": { value: "oklch(0.97 0.001 106.424)" }, - "200": { value: "oklch(0.923 0.003 48.717)" }, - "300": { value: "oklch(0.869 0.005 56.366)" }, - "400": { value: "oklch(0.709 0.01 56.259)" }, - "500": { value: "oklch(0.553 0.013 58.071)" }, - "600": { value: "oklch(0.444 0.011 73.639)" }, - "700": { value: "oklch(0.374 0.01 67.558)" }, - "800": { value: "oklch(0.268 0.007 34.298)" }, - "900": { value: "oklch(0.216 0.006 56.043)" }, - "950": { value: "oklch(0.147 0.004 49.25)" }, - }, + "50": { value: "oklch(0.985 0.004 253)" }, // Original: oklch(0.985 0.002 247.839) + "100": { value: "oklch(0.955 0.006 253)" }, // Original: oklch(0.967 0.003 264.542) + "200": { value: "oklch(0.915 0.01 253)" }, // Original: oklch(0.928 0.006 264.531) + "300": { value: "oklch(0.85 0.016 253)" }, // Original: oklch(0.872 0.01 258.338) + "400": { value: "oklch(0.75 0.025 252)" }, // Original: oklch(0.707 0.022 261.325) + "500": { value: "oklch(0.63 0.042 252)" }, // Original: oklch(0.551 0.027 264.364) + "600": { value: "oklch(0.45 0.055 251)" }, // Original: oklch(0.446 0.03 256.802) + "700": { value: "oklch(0.35 0.045 251)" }, // Original: oklch(0.373 0.034 259.733) + "800": { value: "oklch(0.28 0.035 251)" }, // Original: oklch(0.278 0.033 256.848) + "900": { value: "oklch(0.18 0.03 251)" }, // Original: oklch(0.21 0.034 264.665) + "950": { value: "oklch(0.11 0.025 251)" }, // Original: oklch(0.13 0.028 261.692) }, - }, - semanticTokens: { - colors: { - // Brand colors for consistent theming - brand: generateSemanticTokens("brand"), - // GENERIC STATE - danger: generateSemanticTokens("red"), - info: generateSemanticTokens("blue"), - warning: generateSemanticTokens("amber"), - error: generateSemanticTokens("red"), - // AIRFLOW TASK STATE - active: generateSemanticTokens("blue"), - success: generateSemanticTokens("green"), - failed: generateSemanticTokens("red"), - queued: generateSemanticTokens("stone"), - skipped: generateSemanticTokens("pink"), - up_for_reschedule: generateSemanticTokens("sky"), - up_for_retry: generateSemanticTokens("yellow"), - upstream_failed: generateSemanticTokens("orange"), - running: generateSemanticTokens("cyan"), - restarting: generateSemanticTokens("violet"), - deferred: generateSemanticTokens("purple"), - scheduled: generateSemanticTokens("zinc"), - none: generateSemanticTokens("gray"), - removed: generateSemanticTokens("slate"), - // TAILWIND 4.0 COLORS - red: generateSemanticTokens("red"), - orange: generateSemanticTokens("orange"), - amber: generateSemanticTokens("amber"), - yellow: generateSemanticTokens("yellow"), - lime: generateSemanticTokens("lime"), - green: generateSemanticTokens("green"), - emerald: generateSemanticTokens("emerald"), - teal: generateSemanticTokens("teal"), - cyan: generateSemanticTokens("cyan"), - sky: generateSemanticTokens("sky"), - blue: generateSemanticTokens("blue"), - indigo: generateSemanticTokens("indigo"), - violet: generateSemanticTokens("violet"), - purple: generateSemanticTokens("purple"), - fuchsia: generateSemanticTokens("fuchsia"), - pink: generateSemanticTokens("pink"), - rose: generateSemanticTokens("rose"), - slate: generateSemanticTokens("slate"), - gray: generateSemanticTokens("gray"), - zinc: generateSemanticTokens("zinc"), - neutral: generateSemanticTokens("neutral"), - stone: generateSemanticTokens("stone"), + // TAILWIND 4.0 COLORS + // See https://tailwindcss.com/docs/colors for more information on the colors used here. + red: { + "50": { value: "oklch(0.971 0.013 17.38)" }, + "100": { value: "oklch(0.936 0.032 17.717)" }, + "200": { value: "oklch(0.885 0.062 18.334)" }, + "300": { value: "oklch(0.808 0.114 19.571)" }, + "400": { value: "oklch(0.704 0.191 22.216)" }, + "500": { value: "oklch(0.637 0.237 25.331)" }, + "600": { value: "oklch(0.577 0.245 27.325)" }, + "700": { value: "oklch(0.505 0.213 27.518)" }, + "800": { value: "oklch(0.444 0.177 26.899)" }, + "900": { value: "oklch(0.396 0.141 25.723)" }, + "950": { value: "oklch(0.258 0.092 26.042)" }, + }, + // Values modified from original Tailwind to improve contrast in Chakra UI + orange: { + "50": { value: "oklch(0.982 0.013 83.915)" }, + "100": { value: "oklch(0.961 0.033 82.320)" }, + "200": { value: "oklch(0.918 0.065 79.975)" }, + "300": { value: "oklch(0.857 0.118 76.815)" }, + "400": { value: "oklch(0.7492 0.1439 62.081)" }, // Original: oklch(0.774 0.186 71.555) + "500": { value: "oklch(0.6462 0.1979 43.792)" }, // Original: oklch(0.705 0.213 47.604) + "600": { value: "oklch(0.5902 0.198 35.93)" }, // Original: oklch(0.632 0.214 41.185) + "700": { value: "oklch(0.553 0.184 41.777)" }, + "800": { value: "oklch(0.469 0.144 45.164)" }, + "900": { value: "oklch(0.414 0.110 48.717)" }, + "950": { value: "oklch(0.271 0.069 52.345)" }, + }, + amber: { + "50": { value: "oklch(0.987 0.022 95.277)" }, + "100": { value: "oklch(0.962 0.059 95.617)" }, + "200": { value: "oklch(0.924 0.12 95.746)" }, + "300": { value: "oklch(0.879 0.169 91.605)" }, + "400": { value: "oklch(0.828 0.189 84.429)" }, + "500": { value: "oklch(0.769 0.188 70.08)" }, + "600": { value: "oklch(0.666 0.179 58.318)" }, + "700": { value: "oklch(0.555 0.163 48.998)" }, + "800": { value: "oklch(0.473 0.137 46.201)" }, + "900": { value: "oklch(0.414 0.112 45.904)" }, + "950": { value: "oklch(0.279 0.077 45.635)" }, + }, + yellow: { + "50": { value: "oklch(0.987 0.026 102.212)" }, + "100": { value: "oklch(0.973 0.071 103.193)" }, + "200": { value: "oklch(0.945 0.129 101.54)" }, + "300": { value: "oklch(0.905 0.182 98.111)" }, + "400": { value: "oklch(0.852 0.199 91.936)" }, + "500": { value: "oklch(0.795 0.184 86.047)" }, + "600": { value: "oklch(0.681 0.162 75.834)" }, + "700": { value: "oklch(0.554 0.135 66.442)" }, + "800": { value: "oklch(0.476 0.114 61.907)" }, + "900": { value: "oklch(0.421 0.095 57.708)" }, + "950": { value: "oklch(0.286 0.066 53.813)" }, + }, + lime: { + "50": { value: "oklch(0.986 0.031 120.757)" }, + "100": { value: "oklch(0.967 0.067 122.328)" }, + "200": { value: "oklch(0.938 0.127 124.321)" }, + "300": { value: "oklch(0.897 0.196 126.665)" }, + "400": { value: "oklch(0.841 0.238 128.85)" }, + "500": { value: "oklch(0.768 0.233 130.85)" }, + "600": { value: "oklch(0.648 0.2 131.684)" }, + "700": { value: "oklch(0.532 0.157 131.589)" }, + "800": { value: "oklch(0.453 0.124 130.933)" }, + "900": { value: "oklch(0.405 0.101 131.063)" }, + "950": { value: "oklch(0.274 0.072 132.109)" }, + }, + green: { + // Values modified from original Tailwind to improve contrast in Chakra UI + "50": { value: "oklch(0.982 0.018 155.826)" }, + "100": { value: "oklch(0.962 0.044 156.743)" }, + "200": { value: "oklch(0.925 0.084 155.995)" }, + "300": { value: "oklch(0.75 0.18 153.0)" }, // Original: oklch(0.871 0.15 154.449) + "400": { value: "oklch(0.625 0.209 150.0)" }, // Original: oklch(0.792 0.209 151.711) + "500": { value: "oklch(0.528 0.219 149.579)" }, // Original: oklch(0.723 0.219 149.579) + "600": { value: "oklch(0.47 0.20 149.0)" }, // Original: oklch(0.627 0.194 149.214) + "700": { value: "oklch(0.40 0.16 149.5)" }, // Original: oklch(0.527 0.154 150.069) + "800": { value: "oklch(0.448 0.119 151.328)" }, + "900": { value: "oklch(0.393 0.095 152.535)" }, + "950": { value: "oklch(0.266 0.065 152.934)" }, + }, + emerald: { + "50": { value: "oklch(0.979 0.021 166.113)" }, + "100": { value: "oklch(0.95 0.052 163.051)" }, + "200": { value: "oklch(0.905 0.093 164.15)" }, + "300": { value: "oklch(0.845 0.143 164.978)" }, + "400": { value: "oklch(0.765 0.177 163.223)" }, + "500": { value: "oklch(0.696 0.17 162.48)" }, + "600": { value: "oklch(0.596 0.145 163.225)" }, + "700": { value: "oklch(0.508 0.118 165.612)" }, + "800": { value: "oklch(0.432 0.095 166.913)" }, + "900": { value: "oklch(0.378 0.077 168.94)" }, + "950": { value: "oklch(0.262 0.051 172.552)" }, + }, + teal: { + "50": { value: "oklch(0.984 0.014 180.72)" }, + "100": { value: "oklch(0.953 0.051 180.801)" }, + "200": { value: "oklch(0.91 0.096 180.426)" }, + "300": { value: "oklch(0.855 0.138 181.071)" }, + "400": { value: "oklch(0.777 0.152 181.912)" }, + "500": { value: "oklch(0.704 0.14 182.503)" }, + "600": { value: "oklch(0.6 0.118 184.704)" }, + "700": { value: "oklch(0.511 0.096 186.391)" }, + "800": { value: "oklch(0.437 0.078 188.216)" }, + "900": { value: "oklch(0.386 0.063 188.416)" }, + "950": { value: "oklch(0.277 0.046 192.524)" }, + }, + cyan: { + "50": { value: "oklch(0.984 0.019 200.873)" }, + "100": { value: "oklch(0.956 0.045 203.388)" }, + "200": { value: "oklch(0.917 0.08 205.041)" }, + "300": { value: "oklch(0.865 0.127 207.078)" }, + "400": { value: "oklch(0.789 0.154 211.53)" }, + "500": { value: "oklch(0.715 0.143 215.221)" }, + "600": { value: "oklch(0.609 0.126 221.723)" }, + "700": { value: "oklch(0.52 0.105 223.128)" }, + "800": { value: "oklch(0.45 0.085 224.283)" }, + "900": { value: "oklch(0.398 0.07 227.392)" }, + "950": { value: "oklch(0.302 0.056 229.695)" }, + }, + sky: { + "50": { value: "oklch(0.977 0.013 236.62)" }, + "100": { value: "oklch(0.951 0.026 236.824)" }, + "200": { value: "oklch(0.901 0.058 230.902)" }, + "300": { value: "oklch(0.828 0.111 230.318)" }, + "400": { value: "oklch(0.746 0.16 232.661)" }, + "500": { value: "oklch(0.685 0.169 237.323)" }, + "600": { value: "oklch(0.588 0.158 241.966)" }, + "700": { value: "oklch(0.5 0.134 242.749)" }, + "800": { value: "oklch(0.443 0.11 240.79)" }, + "900": { value: "oklch(0.391 0.09 240.876)" }, + "950": { value: "oklch(0.293 0.066 243.157)" }, + }, + blue: { + "50": { value: "oklch(0.97 0.014 254.604)" }, + "100": { value: "oklch(0.932 0.032 255.585)" }, + "200": { value: "oklch(0.882 0.059 254.128)" }, + "300": { value: "oklch(0.809 0.105 251.813)" }, + "400": { value: "oklch(0.707 0.165 254.624)" }, + "500": { value: "oklch(0.623 0.214 259.815)" }, + "600": { value: "oklch(0.546 0.245 262.881)" }, + "700": { value: "oklch(0.488 0.243 264.376)" }, + "800": { value: "oklch(0.424 0.199 265.638)" }, + "900": { value: "oklch(0.379 0.146 265.522)" }, + "950": { value: "oklch(0.282 0.091 267.935)" }, + }, + indigo: { + "50": { value: "oklch(0.962 0.018 272.314)" }, + "100": { value: "oklch(0.93 0.034 272.788)" }, + "200": { value: "oklch(0.87 0.065 274.039)" }, + "300": { value: "oklch(0.785 0.115 274.713)" }, + "400": { value: "oklch(0.673 0.182 276.935)" }, + "500": { value: "oklch(0.585 0.233 277.117)" }, + "600": { value: "oklch(0.511 0.262 276.966)" }, + "700": { value: "oklch(0.457 0.24 277.023)" }, + "800": { value: "oklch(0.398 0.195 277.366)" }, + "900": { value: "oklch(0.359 0.144 278.697)" }, + "950": { value: "oklch(0.257 0.09 281.288)" }, + }, + violet: { + "50": { value: "oklch(0.969 0.016 293.756)" }, + "100": { value: "oklch(0.943 0.029 294.588)" }, + "200": { value: "oklch(0.894 0.057 293.283)" }, + "300": { value: "oklch(0.811 0.111 293.571)" }, + "400": { value: "oklch(0.702 0.183 293.541)" }, + "500": { value: "oklch(0.606 0.25 292.717)" }, + "600": { value: "oklch(0.541 0.281 293.009)" }, + "700": { value: "oklch(0.491 0.27 292.581)" }, + "800": { value: "oklch(0.432 0.232 292.759)" }, + "900": { value: "oklch(0.38 0.189 293.745)" }, + "950": { value: "oklch(0.283 0.141 291.089)" }, + }, + purple: { + "50": { value: "oklch(0.977 0.014 308.299)" }, + "100": { value: "oklch(0.946 0.033 307.174)" }, + "200": { value: "oklch(0.902 0.063 306.703)" }, + "300": { value: "oklch(0.827 0.119 306.383)" }, + "400": { value: "oklch(0.714 0.203 305.504)" }, + "500": { value: "oklch(0.627 0.265 303.9)" }, + "600": { value: "oklch(0.558 0.288 302.321)" }, + "700": { value: "oklch(0.496 0.265 301.924)" }, + "800": { value: "oklch(0.438 0.218 303.724)" }, + "900": { value: "oklch(0.381 0.176 304.987)" }, + "950": { value: "oklch(0.291 0.149 302.717)" }, + }, + fuchsia: { + "50": { value: "oklch(0.977 0.017 320.058)" }, + "100": { value: "oklch(0.952 0.037 318.852)" }, + "200": { value: "oklch(0.903 0.076 319.62)" }, + "300": { value: "oklch(0.833 0.145 321.434)" }, + "400": { value: "oklch(0.74 0.238 322.16)" }, + "500": { value: "oklch(0.667 0.295 322.15)" }, + "600": { value: "oklch(0.591 0.293 322.896)" }, + "700": { value: "oklch(0.518 0.253 323.949)" }, + "800": { value: "oklch(0.452 0.211 324.591)" }, + "900": { value: "oklch(0.401 0.17 325.612)" }, + "950": { value: "oklch(0.293 0.136 325.661)" }, + }, + pink: { + "50": { value: "oklch(0.971 0.014 343.198)" }, + "100": { value: "oklch(0.948 0.028 342.258)" }, + "200": { value: "oklch(0.899 0.061 343.231)" }, + "300": { value: "oklch(0.823 0.12 346.018)" }, + "400": { value: "oklch(0.718 0.202 349.761)" }, + "500": { value: "oklch(0.656 0.241 354.308)" }, + "600": { value: "oklch(0.592 0.249 0.584)" }, + "700": { value: "oklch(0.525 0.223 3.958)" }, + "800": { value: "oklch(0.459 0.187 3.815)" }, + "900": { value: "oklch(0.408 0.153 2.432)" }, + "950": { value: "oklch(0.284 0.109 3.907)" }, + }, + rose: { + "50": { value: "oklch(0.969 0.015 12.422)" }, + "100": { value: "oklch(0.941 0.03 12.58)" }, + "200": { value: "oklch(0.892 0.058 10.001)" }, + "300": { value: "oklch(0.81 0.117 11.638)" }, + "400": { value: "oklch(0.712 0.194 13.428)" }, + "500": { value: "oklch(0.645 0.246 16.439)" }, + "600": { value: "oklch(0.586 0.253 17.585)" }, + "700": { value: "oklch(0.514 0.222 16.935)" }, + "800": { value: "oklch(0.455 0.188 13.697)" }, + "900": { value: "oklch(0.41 0.159 10.272)" }, + "950": { value: "oklch(0.271 0.105 12.094)" }, + }, + slate: { + "50": { value: "oklch(0.984 0.003 247.858)" }, + "100": { value: "oklch(0.968 0.007 247.896)" }, + "200": { value: "oklch(0.929 0.013 255.508)" }, + "300": { value: "oklch(0.869 0.022 252.894)" }, + "400": { value: "oklch(0.704 0.04 256.788)" }, + "500": { value: "oklch(0.554 0.046 257.417)" }, + "600": { value: "oklch(0.446 0.043 257.281)" }, + "700": { value: "oklch(0.372 0.044 257.287)" }, + "800": { value: "oklch(0.279 0.041 260.031)" }, + "900": { value: "oklch(0.208 0.042 265.755)" }, + "950": { value: "oklch(0.129 0.042 264.695)" }, + }, + zinc: { + "50": { value: "oklch(0.985 0 0)" }, + "100": { value: "oklch(0.967 0.001 286.375)" }, + "200": { value: "oklch(0.92 0.004 286.32)" }, + "300": { value: "oklch(0.871 0.006 286.286)" }, + "400": { value: "oklch(0.705 0.015 286.067)" }, + "500": { value: "oklch(0.552 0.016 285.938)" }, + "600": { value: "oklch(0.442 0.017 285.786)" }, + "700": { value: "oklch(0.37 0.013 285.805)" }, + "800": { value: "oklch(0.274 0.006 286.033)" }, + "900": { value: "oklch(0.21 0.006 285.885)" }, + "950": { value: "oklch(0.141 0.005 285.823)" }, + }, + neutral: { + "50": { value: "oklch(0.985 0 0)" }, + "100": { value: "oklch(0.97 0 0)" }, + "200": { value: "oklch(0.922 0 0)" }, + "300": { value: "oklch(0.87 0 0)" }, + "400": { value: "oklch(0.708 0 0)" }, + "500": { value: "oklch(0.556 0 0)" }, + "600": { value: "oklch(0.439 0 0)" }, + "700": { value: "oklch(0.371 0 0)" }, + "800": { value: "oklch(0.269 0 0)" }, + "900": { value: "oklch(0.205 0 0)" }, + "950": { value: "oklch(0.145 0 0)" }, + }, + stone: { + "50": { value: "oklch(0.985 0.001 106.423)" }, + "100": { value: "oklch(0.97 0.001 106.424)" }, + "200": { value: "oklch(0.923 0.003 48.717)" }, + "300": { value: "oklch(0.869 0.005 56.366)" }, + "400": { value: "oklch(0.709 0.01 56.259)" }, + "500": { value: "oklch(0.553 0.013 58.071)" }, + "600": { value: "oklch(0.444 0.011 73.639)" }, + "700": { value: "oklch(0.374 0.01 67.558)" }, + "800": { value: "oklch(0.268 0.007 34.298)" }, + "900": { value: "oklch(0.216 0.006 56.043)" }, + "950": { value: "oklch(0.147 0.004 49.25)" }, }, }, }, -}); + semanticTokens: { + colors: { + // Brand colors for consistent theming + brand: generateSemanticTokens("brand"), + // GENERIC STATE + danger: generateSemanticTokens("red"), + info: generateSemanticTokens("blue"), + warning: generateSemanticTokens("amber"), + error: generateSemanticTokens("red"), + // AIRFLOW TASK STATE + active: generateSemanticTokens("blue"), + success: generateSemanticTokens("green"), + failed: generateSemanticTokens("red"), + queued: generateSemanticTokens("stone"), + skipped: generateSemanticTokens("pink"), + up_for_reschedule: generateSemanticTokens("sky"), + up_for_retry: generateSemanticTokens("yellow"), + upstream_failed: generateSemanticTokens("orange"), + running: generateSemanticTokens("cyan"), + restarting: generateSemanticTokens("violet"), + deferred: generateSemanticTokens("purple"), + scheduled: generateSemanticTokens("zinc"), + none: generateSemanticTokens("gray"), + removed: generateSemanticTokens("slate"), + // TAILWIND 4.0 COLORS + red: generateSemanticTokens("red"), + orange: generateSemanticTokens("orange"), + amber: generateSemanticTokens("amber"), + yellow: generateSemanticTokens("yellow"), + lime: generateSemanticTokens("lime"), + green: generateSemanticTokens("green"), + emerald: generateSemanticTokens("emerald"), + teal: generateSemanticTokens("teal"), + cyan: generateSemanticTokens("cyan"), + sky: generateSemanticTokens("sky"), + blue: generateSemanticTokens("blue"), + indigo: generateSemanticTokens("indigo"), + violet: generateSemanticTokens("violet"), + purple: generateSemanticTokens("purple"), + fuchsia: generateSemanticTokens("fuchsia"), + pink: generateSemanticTokens("pink"), + rose: generateSemanticTokens("rose"), + slate: generateSemanticTokens("slate"), + gray: generateSemanticTokens("gray"), + zinc: generateSemanticTokens("zinc"), + neutral: generateSemanticTokens("neutral"), + stone: generateSemanticTokens("stone"), + }, + }, +}; + +export const createTheme = (userTheme?: Theme) => { + const customConfig = defineConfig({ + theme: typeof userTheme === "undefined" ? defaultTheme : { ...defaultTheme, ...userTheme }, + }); -export const system = createSystem(defaultConfig, customConfig); + return createSystem(defaultConfig, customConfig); +}; // Utility function to resolve CSS variables to their computed values // See: https://github.com/chakra-ui/panda/discussions/2200 From b6550b16ef6eb8bb10a564b8c696092d8284086b Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Sun, 16 Nov 2025 21:37:05 +0100 Subject: [PATCH 3/8] Add Oklch type and unit tests --- .../src/airflow/api_fastapi/common/types.py | 58 ++++++++++++++++++- .../core_api/openapi/_private_ui.yaml | 4 +- .../src/airflow/config_templates/config.yml | 9 ++- .../ui/openapi-gen/requests/schemas.gen.ts | 6 +- .../ui/openapi-gen/requests/types.gen.ts | 4 +- .../unit/api_fastapi/common/test_types.py | 58 +++++++++++++++++++ 6 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 airflow-core/tests/unit/api_fastapi/common/test_types.py diff --git a/airflow-core/src/airflow/api_fastapi/common/types.py b/airflow-core/src/airflow/api_fastapi/common/types.py index 9dbf81fd43461..e655d52b72cff 100644 --- a/airflow-core/src/airflow/api_fastapi/common/types.py +++ b/airflow-core/src/airflow/api_fastapi/common/types.py @@ -16,6 +16,7 @@ # under the License. from __future__ import annotations +import re from dataclasses import dataclass from datetime import timedelta from enum import Enum @@ -28,6 +29,9 @@ BaseModel, BeforeValidator, ConfigDict, + field_validator, + model_serializer, + model_validator, ) from airflow._shared.timezones import timezone @@ -109,6 +113,58 @@ class UIAlert(BaseModel): category: Literal["info", "warning", "error"] +class OklchColor(BaseModel): + """Validates OKLCH color format from string oklch(l c h).""" + + lightness: float + chroma: float + hue: float + + @model_validator(mode="before") + @classmethod + def parse_oklch_string(cls, data): + if isinstance(data, str): + oklch_regex_pattern = r"^oklch\((-?[\d.]+) (-?[\d.]+) (-?[\d.]+)\)$" + match = re.match(oklch_regex_pattern, data) + + if not match: + raise ValueError(f"Invalid OKLCH format: {data} Expected format oklch(l c h)") + + ligthness_str, chroma_str, hue_str = match.groups() + + return { + "lightness": float(ligthness_str), + "chroma": float(chroma_str), + "hue": float(hue_str), + } + return data + + @field_validator("lightness") + @classmethod + def validate_lightness(cls, value: float) -> float: + if value < 0 or value > 1: + raise ValueError(f"Invalid lightness: {value} Must be between 0 and 1") + return value + + @field_validator("chroma") + @classmethod + def validate_chroma(cls, value: float) -> float: + if value < 0 or value > 0.5: + raise ValueError(f"Invalid chroma: {value} Must be between 0 and 0.5") + return value + + @field_validator("hue") + @classmethod + def validate_hue(cls, value: float) -> float: + if value < 0 or value > 360: + raise ValueError(f"Invalid hue: {value} Must be between 0 and 360") + return value + + @model_serializer(mode="plain") + def serialize_model(self) -> str: + return f"oklch({self.lightness} {self.chroma} {self.hue})" + + class Theme(BaseModel): """JSON to modify Chakra's theme.""" @@ -118,7 +174,7 @@ class Theme(BaseModel): Literal["brand"], dict[ Literal["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"], - dict[Literal["value"], str], + dict[Literal["value"], OklchColor], ], ], ] diff --git a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml index 858d5f8aeeba1..711c7ba4e16e5 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml +++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml @@ -2300,6 +2300,8 @@ components: - type title: NodeResponse description: Node serializer for responses. + OklchColor: + type: string ReprocessBehavior: type: string enum: @@ -2660,7 +2662,7 @@ components: additionalProperties: additionalProperties: additionalProperties: - type: string + $ref: '#/components/schemas/OklchColor' propertyNames: const: value type: object diff --git a/airflow-core/src/airflow/config_templates/config.yml b/airflow-core/src/airflow/config_templates/config.yml index ef4291e06a956..ce99dd7c8fe1c 100644 --- a/airflow-core/src/airflow/config_templates/config.yml +++ b/airflow-core/src/airflow/config_templates/config.yml @@ -1364,7 +1364,14 @@ api: JSON config to customiza the Chakra UI theme. Currently only supports ``brand`` color customization. - Must supply ``50``-``950`` color values for ``brand`` color. + Must supply ``50``-``950`` OKLCH color values for ``brand`` color. + + .. important:: + oklch(l c h) must follow next format: + + * l (lightness) : float Must be between 0 and 1 + * c (chroma) : float Must be between 0 and 0.5 + * h (hue) : float Must be between 0 and 360 Note: As shown below, you can split your json config over multiple lines by indenting. See configparser documentation for an example: diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts index 45bb74e314751..440ea0b902b1c 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts @@ -8071,6 +8071,10 @@ export const $NodeResponse = { description: 'Node serializer for responses.' } as const; +export const $OklchColor = { + type: 'string' +} as const; + export const $StandardHookFields = { properties: { description: { @@ -8264,7 +8268,7 @@ export const $Theme = { additionalProperties: { additionalProperties: { additionalProperties: { - type: 'string' + '$ref': '#/components/schemas/OklchColor' }, propertyNames: { const: 'value' diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts index 2e6cfa365980b..da93b544463fd 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts @@ -2000,6 +2000,8 @@ export type NodeResponse = { asset_condition_type?: 'or-gate' | 'and-gate' | null; }; +export type OklchColor = string; + /** * Standard fields of a Hook that a form will render. */ @@ -2062,7 +2064,7 @@ export type Theme = { [key: string]: { [key: string]: { [key: string]: { - [key: string]: (string); + [key: string]: OklchColor; }; }; }; diff --git a/airflow-core/tests/unit/api_fastapi/common/test_types.py b/airflow-core/tests/unit/api_fastapi/common/test_types.py new file mode 100644 index 0000000000000..d3cc740f42ce9 --- /dev/null +++ b/airflow-core/tests/unit/api_fastapi/common/test_types.py @@ -0,0 +1,58 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +from __future__ import annotations + +import pytest +from pydantic import ValidationError + +from airflow.api_fastapi.common.types import OklchColor + + +class TestOklchColor: + def test_valid_oklch(self): + color_str = "oklch(0.637 0.237 25.331)" + color = OklchColor.model_validate(color_str) + assert color.lightness == pytest.approx(0.637) + assert color.chroma == pytest.approx(0.237) + assert color.hue == pytest.approx(25.331) + assert color.model_dump() == color_str + + @pytest.mark.parametrize( + ("input_str", "error_message"), + [ + ("oklch(-0.1 0.15 240)", "Invalid lightness: -0.1 Must be between 0 and 1"), + ("oklch(1.5 0.15 240)", "Invalid lightness: 1.5 Must be between 0 and 1"), + ("oklch(0.5 -0.1 240)", "Invalid chroma: -0.1 Must be between 0 and 0.5"), + ("oklch(0.5 0.6 240)", "Invalid chroma: 0.6 Must be between 0 and 0.5"), + ("oklch(0.5 0.15 -10)", "Invalid hue: -10.0 Must be between 0 and 360"), + ("oklch(0.5 0.15 400)", "Invalid hue: 400.0 Must be between 0 and 360"), + ("rgb(255, 0, 0)", "Invalid OKLCH format: rgb(255, 0, 0) Expected format oklch(l c h)"), + ("oklch(0.5 0.15)", "Invalid OKLCH format: oklch(0.5 0.15) Expected format oklch(l c h)"), + ( + "oklch(0.5 0.15 240 0.5)", + "Invalid OKLCH format: oklch(0.5 0.15 240 0.5) Expected format oklch(l c h)", + ), + ( + "oklch(abc 0.15 240)", + "Invalid OKLCH format: oklch(abc 0.15 240) Expected format oklch(l c h)", + ), + ], + ) + def test_invalid_oklch(self, input_str, error_message): + with pytest.raises(ValidationError) as exc_info: + OklchColor.model_validate(input_str) + assert error_message in str(exc_info.value) From 174b3973a84a800364d94b022aae6e71570ee1c5 Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:37:37 +0100 Subject: [PATCH 4/8] Add UI documentation --- airflow-core/docs/howto/customize-ui.rst | 89 ++++++++++++++++++ .../exmaple_theme_configuration_dark_mode.png | Bin 0 -> 60070 bytes ...exmaple_theme_configuration_light_mode.png | Bin 0 -> 60098 bytes .../src/airflow/config_templates/config.yml | 71 +++++++------- docs/spelling_wordlist.txt | 1 + 5 files changed, 126 insertions(+), 35 deletions(-) create mode 100644 airflow-core/docs/img/change-theme/exmaple_theme_configuration_dark_mode.png create mode 100644 airflow-core/docs/img/change-theme/exmaple_theme_configuration_light_mode.png diff --git a/airflow-core/docs/howto/customize-ui.rst b/airflow-core/docs/howto/customize-ui.rst index 7d037d2485ff3..2833f389ca5c8 100644 --- a/airflow-core/docs/howto/customize-ui.rst +++ b/airflow-core/docs/howto/customize-ui.rst @@ -61,6 +61,95 @@ After .. image:: ../img/change-site-title/example_instance_name_configuration.png +.. _customizing-ui-theme: + +Customizing UI theme +-------------------- + +We can provide a JSON configuration to customize the UI. + +.. important:: + + - Currently only the ``brand`` color palette can be customized. + - You must supply ``50``-``950`` OKLCH color values for ``brand`` color. + - OKLCH colors must have next format ``oklch(l c h)`` For more info see :ref:`config:api__theme` + +.. note:: + + Modifying the ``brand`` color palette you also modify the navbar/sidebar. + +To customize the UI, simply: + +1. Add the configuration option of ``theme`` under the ``[api]`` section inside ``airflow.cfg``: + +.. code-block:: + + [api] + + theme = { + "tokens": { + "colors": { + "brand": { + "50": { "value": "oklch(0.971 0.013 17.38)" }, + "100": { "value": "oklch(0.936 0.032 17.717)" }, + "200": { "value": "oklch(0.885 0.062 18.334)" }, + "300": { "value": "oklch(0.808 0.114 19.571)" }, + "400": { "value": "oklch(0.704 0.191 22.216)" }, + "500": { "value": "oklch(0.637 0.237 25.331)" }, + "600": { "value": "oklch(0.577 0.245 27.325)" }, + "700": { "value": "oklch(0.505 0.213 27.518)" }, + "800": { "value": "oklch(0.444 0.177 26.899)" }, + "900": { "value": "oklch(0.396 0.141 25.723)" }, + "950": { "value": "oklch(0.258 0.092 26.042)" } + } + } + } + } + + +.. note:: + + The whitespace, particularly on the last line, is important so a multi-line value works properly. More details can be found in the + the `configparser docs `_. + +2. Alternatively, you can set a custom title using the environment variable: + +.. code-block:: + + AIRFLOW__API__THEME='{ + "tokens": { + "colors": { + "brand": { + "50": { "value": "oklch(0.971 0.013 17.38)" }, + "100": { "value": "oklch(0.936 0.032 17.717)" }, + "200": { "value": "oklch(0.885 0.062 18.334)" }, + "300": { "value": "oklch(0.808 0.114 19.571)" }, + "400": { "value": "oklch(0.704 0.191 22.216)" }, + "500": { "value": "oklch(0.637 0.237 25.331)" }, + "600": { "value": "oklch(0.577 0.245 27.325)" }, + "700": { "value": "oklch(0.505 0.213 27.518)" }, + "800": { "value": "oklch(0.444 0.177 26.899)" }, + "900": { "value": "oklch(0.396 0.141 25.723)" }, + "950": { "value": "oklch(0.258 0.092 26.042)" } + } + } + } + }' + + +Screenshots +^^^^^^^^^^^ + +Light Mode +"""""""""" + +.. image:: ../img/change-theme/exmaple_theme_configuration_light_mode.png + +Dark Mode +""""""""" + +.. image:: ../img/change-theme/exmaple_theme_configuration_dark_mode.png + | Adding Dashboard Alert Messages diff --git a/airflow-core/docs/img/change-theme/exmaple_theme_configuration_dark_mode.png b/airflow-core/docs/img/change-theme/exmaple_theme_configuration_dark_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..26dc6194b48abf47e53324decc6e831d770192b2 GIT binary patch literal 60070 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 J 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 Id29Cm0jtzMi~s-t literal 0 HcmV?d00001 diff --git a/airflow-core/docs/img/change-theme/exmaple_theme_configuration_light_mode.png b/airflow-core/docs/img/change-theme/exmaple_theme_configuration_light_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..95deb9bcf1c4e1726d0c5bf6dc77c3df4e59cdca GIT binary patch literal 60098 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 =IQa9I_q_lA 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 A`~Uy| literal 0 HcmV?d00001 diff --git a/airflow-core/src/airflow/config_templates/config.yml b/airflow-core/src/airflow/config_templates/config.yml index ce99dd7c8fe1c..db3afb7cfdc79 100644 --- a/airflow-core/src/airflow/config_templates/config.yml +++ b/airflow-core/src/airflow/config_templates/config.yml @@ -1361,17 +1361,18 @@ api: default: theme: description: | - JSON config to customiza the Chakra UI theme. + JSON config to customize the Chakra UI theme. Currently only supports ``brand`` color customization. Must supply ``50``-``950`` OKLCH color values for ``brand`` color. + For usage see :ref:`customizing-ui-theme` .. important:: - oklch(l c h) must follow next format: + ``oklch(l c h)`` must follow next format: - * l (lightness) : float Must be between 0 and 1 - * c (chroma) : float Must be between 0 and 0.5 - * h (hue) : float Must be between 0 and 360 + - l (lightness) : ``float`` Must be between 0 and 1 + - c (chroma) : ``float`` Must be between 0 and 0.5 + - h (hue) : ``float`` Must be between 0 and 360 Note: As shown below, you can split your json config over multiple lines by indenting. See configparser documentation for an example: @@ -1380,44 +1381,44 @@ api: type: string example: > { - "tokens": { - "colors": { - "brand": { - "50": { "value": "oklch(0.971 0.013 17.38)" }, - "100": { "value": "oklch(0.936 0.032 17.717)" }, - "200": { "value": "oklch(0.885 0.062 18.334)" }, - "300": { "value": "oklch(0.808 0.114 19.571)" }, - "400": { "value": "oklch(0.704 0.191 22.216)" }, - "500": { "value": "oklch(0.637 0.237 25.331)" }, - "600": { "value": "oklch(0.577 0.245 27.325)" }, - "700": { "value": "oklch(0.505 0.213 27.518)" }, - "800": { "value": "oklch(0.444 0.177 26.899)" }, - "900": { "value": "oklch(0.396 0.141 25.723)" }, - "950": { "value": "oklch(0.258 0.092 26.042)" } + "tokens": { + "colors": { + "brand": { + "50": { "value": "oklch(0.971 0.013 17.38)" }, + "100": { "value": "oklch(0.936 0.032 17.717)" }, + "200": { "value": "oklch(0.885 0.062 18.334)" }, + "300": { "value": "oklch(0.808 0.114 19.571)" }, + "400": { "value": "oklch(0.704 0.191 22.216)" }, + "500": { "value": "oklch(0.637 0.237 25.331)" }, + "600": { "value": "oklch(0.577 0.245 27.325)" }, + "700": { "value": "oklch(0.505 0.213 27.518)" }, + "800": { "value": "oklch(0.444 0.177 26.899)" }, + "900": { "value": "oklch(0.396 0.141 25.723)" }, + "950": { "value": "oklch(0.258 0.092 26.042)" } + } } } } - } default: > {{ - "tokens": {{ - "colors": {{ - "brand": {{ - "50": {{ "value": "oklch(0.98 0.006 248.717)" }}, - "100": {{ "value": "oklch(0.962 0.012 249.460)" }}, - "200": {{ "value": "oklch(0.923 0.023 255.082)" }}, - "300": {{ "value": "oklch(0.865 0.039 252.420)" }}, - "400": {{ "value": "oklch(0.705 0.066 256.378)" }}, - "500": {{ "value": "oklch(0.575 0.08 257.759)" }}, - "600": {{ "value": "oklch(0.469 0.084 257.657)" }}, - "700": {{ "value": "oklch(0.399 0.084 257.850)" }}, - "800": {{ "value": "oklch(0.324 0.072 260.329)" }}, - "900": {{ "value": "oklch(0.259 0.062 265.566)" }}, - "950": {{ "value": "oklch(0.179 0.05 265.487)" }} + "tokens": {{ + "colors": {{ + "brand": {{ + "50": {{ "value": "oklch(0.98 0.006 248.717)" }}, + "100": {{ "value": "oklch(0.962 0.012 249.460)" }}, + "200": {{ "value": "oklch(0.923 0.023 255.082)" }}, + "300": {{ "value": "oklch(0.865 0.039 252.420)" }}, + "400": {{ "value": "oklch(0.705 0.066 256.378)" }}, + "500": {{ "value": "oklch(0.575 0.08 257.759)" }}, + "600": {{ "value": "oklch(0.469 0.084 257.657)" }}, + "700": {{ "value": "oklch(0.399 0.084 257.850)" }}, + "800": {{ "value": "oklch(0.324 0.072 260.329)" }}, + "900": {{ "value": "oklch(0.259 0.062 265.566)" }}, + "950": {{ "value": "oklch(0.179 0.05 265.487)" }} + }} }} }} }} - }} enable_swagger_ui: description: | Boolean for running SwaggerUI in the webserver. diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index de8c0cabf2430..23e4be77cea91 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -1253,6 +1253,7 @@ od odbc odps ok +oklch Okta okta onboarded From cc18faba5959a7e108309090e1eccbbd305dda1d Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Wed, 19 Nov 2025 23:35:34 +0100 Subject: [PATCH 5/8] Fix CI test_config --- .../api_fastapi/core_api/routes/ui/config.py | 22 +++++++-------- .../core_api/routes/ui/test_config.py | 27 +++++++++---------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py index 7ff796d69c230..98e506af0c1be 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py @@ -37,17 +37,17 @@ "tokens": { "colors": { "brand": { - "50": { "value": "oklch(0.971 0.013 17.38)" }, - "100": { "value": "oklch(0.936 0.032 17.717)" }, - "200": { "value": "oklch(0.885 0.062 18.334)" }, - "300": { "value": "oklch(0.808 0.114 19.571)" }, - "400": { "value": "oklch(0.704 0.191 22.216)" }, - "500": { "value": "oklch(0.637 0.237 25.331)" }, - "600": { "value": "oklch(0.577 0.245 27.325)" }, - "700": { "value": "oklch(0.505 0.213 27.518)" }, - "800": { "value": "oklch(0.444 0.177 26.899)" }, - "900": { "value": "oklch(0.396 0.141 25.723)" }, - "950": { "value": "oklch(0.258 0.092 26.042)" } + "50": { "value": "oklch(0.98 0.006 248.717)" }, + "100": { "value": "oklch(0.962 0.012 249.46)" }, + "200": { "value": "oklch(0.923 0.023 255.082)" }, + "300": { "value": "oklch(0.865 0.039 252.42)" }, + "400": { "value": "oklch(0.705 0.066 256.378)" }, + "500": { "value": "oklch(0.575 0.08 257.759)" }, + "600": { "value": "oklch(0.469 0.084 257.657)" }, + "700": { "value": "oklch(0.399 0.084 257.85)" }, + "800": { "value": "oklch(0.324 0.072 260.329)" }, + "900": { "value": "oklch(0.259 0.062 265.566)" }, + "950": { "value": "oklch(0.179 0.05 265.487)" } } } } diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py index bca3a28614d7f..3c5077b0b5b0c 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py @@ -23,27 +23,25 @@ pytestmark = pytest.mark.db_test -THEME = """ -{ +THEME = { "tokens": { "colors": { "brand": { - "50": { "value": "oklch(0.971 0.013 17.38)" }, - "100": { "value": "oklch(0.936 0.032 17.717)" }, - "200": { "value": "oklch(0.885 0.062 18.334)" }, - "300": { "value": "oklch(0.808 0.114 19.571)" }, - "400": { "value": "oklch(0.704 0.191 22.216)" }, - "500": { "value": "oklch(0.637 0.237 25.331)" }, - "600": { "value": "oklch(0.577 0.245 27.325)" }, - "700": { "value": "oklch(0.505 0.213 27.518)" }, - "800": { "value": "oklch(0.444 0.177 26.899)" }, - "900": { "value": "oklch(0.396 0.141 25.723)" }, - "950": { "value": "oklch(0.258 0.092 26.042)" } + "50": {"value": "oklch(0.98 0.006 248.717)"}, + "100": {"value": "oklch(0.962 0.012 249.46)"}, + "200": {"value": "oklch(0.923 0.023 255.082)"}, + "300": {"value": "oklch(0.865 0.039 252.42)"}, + "400": {"value": "oklch(0.705 0.066 256.378)"}, + "500": {"value": "oklch(0.575 0.08 257.759)"}, + "600": {"value": "oklch(0.469 0.084 257.657)"}, + "700": {"value": "oklch(0.399 0.084 257.85)"}, + "800": {"value": "oklch(0.324 0.072 260.329)"}, + "900": {"value": "oklch(0.259 0.062 265.566)"}, + "950": {"value": "oklch(0.179 0.05 265.487)"}, } } } } -""" mock_config_response = { "page_size": 100, @@ -75,7 +73,6 @@ def mock_config_data(): ("api", "default_wrap"): "false", ("api", "auto_refresh_interval"): "3", ("api", "require_confirmation_dag_change"): "false", - ("api", "theme"): THEME, } ): yield From 5bbfe6afa9f9a9f8e275da238656619a1575b82c Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:11:56 +0100 Subject: [PATCH 6/8] Fix Oklch regex --- .../src/airflow/api_fastapi/common/types.py | 2 +- .../unit/api_fastapi/common/test_types.py | 32 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/types.py b/airflow-core/src/airflow/api_fastapi/common/types.py index e655d52b72cff..a09f1c9b383ff 100644 --- a/airflow-core/src/airflow/api_fastapi/common/types.py +++ b/airflow-core/src/airflow/api_fastapi/common/types.py @@ -124,7 +124,7 @@ class OklchColor(BaseModel): @classmethod def parse_oklch_string(cls, data): if isinstance(data, str): - oklch_regex_pattern = r"^oklch\((-?[\d.]+) (-?[\d.]+) (-?[\d.]+)\)$" + oklch_regex_pattern = r"^oklch\((-?\d+(?:\.\d+)?) (-?\d+(?:\.\d+)?) (-?\d+(?:\.\d+)?)\)$" match = re.match(oklch_regex_pattern, data) if not match: diff --git a/airflow-core/tests/unit/api_fastapi/common/test_types.py b/airflow-core/tests/unit/api_fastapi/common/test_types.py index d3cc740f42ce9..c56e0a2e62995 100644 --- a/airflow-core/tests/unit/api_fastapi/common/test_types.py +++ b/airflow-core/tests/unit/api_fastapi/common/test_types.py @@ -23,13 +23,19 @@ class TestOklchColor: - def test_valid_oklch(self): - color_str = "oklch(0.637 0.237 25.331)" - color = OklchColor.model_validate(color_str) - assert color.lightness == pytest.approx(0.637) - assert color.chroma == pytest.approx(0.237) - assert color.hue == pytest.approx(25.331) - assert color.model_dump() == color_str + @pytest.mark.parametrize( + ("input_str", "expected"), + [ + ("oklch(0.637 0.237 25.331)", (0.637, 0.237, 25.331, "oklch(0.637 0.237 25.331)")), + ("oklch(1 0.230 25.331)", (1.0, 0.23, 25.331, "oklch(1.0 0.23 25.331)")), + ], + ) + def test_valid_oklch(self, input_str, expected): + color = OklchColor.model_validate(input_str) + assert color.lightness == pytest.approx(expected[0]) + assert color.chroma == pytest.approx(expected[1]) + assert color.hue == pytest.approx(expected[2]) + assert color.model_dump() == expected[3] @pytest.mark.parametrize( ("input_str", "error_message"), @@ -50,6 +56,18 @@ def test_valid_oklch(self): "oklch(abc 0.15 240)", "Invalid OKLCH format: oklch(abc 0.15 240) Expected format oklch(l c h)", ), + ( + "oklch(10 0. 240)", + "Invalid OKLCH format: oklch(10 0. 240) Expected format oklch(l c h)", + ), + ( + "oklch(10 3 .240)", + "Invalid OKLCH format: oklch(10 3 .240) Expected format oklch(l c h)", + ), + ( + "oklch(. 3 240)", + "Invalid OKLCH format: oklch(. 3 240) Expected format oklch(l c h)", + ), ], ) def test_invalid_oklch(self, input_str, error_message): From 0dfc2ca97f47cf54d84b2683cbaf061f16f5ba75 Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:33:44 +0100 Subject: [PATCH 7/8] Move ChakraCustomProvider.tsx --- .../ChakraCustomProvider.tsx | 0 .../ui/src/context/chakraCustom/index.ts | 20 ------------------- airflow-core/src/airflow/ui/src/main.tsx | 2 +- 3 files changed, 1 insertion(+), 21 deletions(-) rename airflow-core/src/airflow/ui/src/context/{chakraCustom => }/ChakraCustomProvider.tsx (100%) delete mode 100644 airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts diff --git a/airflow-core/src/airflow/ui/src/context/chakraCustom/ChakraCustomProvider.tsx b/airflow-core/src/airflow/ui/src/context/ChakraCustomProvider.tsx similarity index 100% rename from airflow-core/src/airflow/ui/src/context/chakraCustom/ChakraCustomProvider.tsx rename to airflow-core/src/airflow/ui/src/context/ChakraCustomProvider.tsx diff --git a/airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts b/airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts deleted file mode 100644 index 0ad3c5ef2293b..0000000000000 --- a/airflow-core/src/airflow/ui/src/context/chakraCustom/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/*! - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export * from "./ChakraCustomProvider"; diff --git a/airflow-core/src/airflow/ui/src/main.tsx b/airflow-core/src/airflow/ui/src/main.tsx index 6ed6f3dba7613..abd40e064d478 100644 --- a/airflow-core/src/airflow/ui/src/main.tsx +++ b/airflow-core/src/airflow/ui/src/main.tsx @@ -28,7 +28,7 @@ import * as ReactRouterDOM from "react-router-dom"; import * as ReactJSXRuntime from "react/jsx-runtime"; import type { HTTPExceptionResponse } from "openapi/requests/types.gen"; -import { ChakraCustomProvider } from "src/context/chakraCustom"; +import { ChakraCustomProvider } from "src/context/ChakraCustomProvider"; import { ColorModeProvider } from "src/context/colorMode"; import { TimezoneProvider } from "src/context/timezone"; import { router } from "src/router"; From 1a973771cd93720f5fd00171c09c6d164b2e99d1 Mon Sep 17 00:00:00 2001 From: Zinkue <104169193+Zinkue@users.noreply.github.com> Date: Wed, 10 Dec 2025 21:43:10 +0100 Subject: [PATCH 8/8] Create defaultSystem --- .../airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx | 5 ++--- airflow-core/src/airflow/ui/src/theme.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx index bf925e7fcdee6..85f0fc4ec0e94 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx @@ -42,7 +42,7 @@ import type { TaskInstanceResponse } from "openapi/requests/types.gen"; import { TaskTrySelect } from "src/components/TaskTrySelect"; import { Button, Menu, Select, Tooltip } from "src/components/ui"; import { SearchParamsKeys } from "src/constants/searchParams"; -import { createTheme } from "src/theme"; +import { defaultSystem } from "src/theme"; import { type LogLevel, logLevelColorMapping, logLevelOptions } from "src/utils/logs"; type Props = { @@ -85,12 +85,11 @@ export const TaskLogHeader = ({ const sources = searchParams.getAll(SearchParamsKeys.SOURCE); const logLevels = searchParams.getAll(SearchParamsKeys.LOG_LEVEL); const hasLogLevels = logLevels.length > 0; - const system = createTheme(); // Have select zIndex greater than modal zIndex in fullscreen so that // select options are displayed. const zIndex = isFullscreen - ? Number(system.tokens.categoryMap.get("zIndex")?.get("modal")?.value ?? 1400) + 1 + ? Number(defaultSystem.tokens.categoryMap.get("zIndex")?.get("modal")?.value ?? 1400) + 1 : undefined; const sourceOptionList = createListCollection<{ diff --git a/airflow-core/src/airflow/ui/src/theme.ts b/airflow-core/src/airflow/ui/src/theme.ts index 5bf6a0f7e8e0c..266c590a3d043 100644 --- a/airflow-core/src/airflow/ui/src/theme.ts +++ b/airflow-core/src/airflow/ui/src/theme.ts @@ -405,6 +405,8 @@ export const createTheme = (userTheme?: Theme) => { return createSystem(defaultConfig, customConfig); }; +export const defaultSystem = createTheme(); + // Utility function to resolve CSS variables to their computed values // See: https://github.com/chakra-ui/panda/discussions/2200 export const getComputedCSSVariableValue = (variable: string): string =>