mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-14 09:55:12 +00:00
feat(login): update to nextjs 16, improve caching, change default colors (#11629)
# Which Problems Are Solved Updated the project to use the latest versions of Next.js and React, and implemented a SWR caching strategy for key settings API calls to improve performance. Adjusted configuration to ensure compatibility with Next.js 16. # How the Problems Are Solved - Updated next to 16.1.6, react & react-dom to 19.2.4, and next-intl to 4.8.3. - Implemented `API_CACHE_ENABLED` and `API_CACHE_CONFIG` (replacing deprecated unstable_cacheLife) for: - getBrandingSettings - getSecuritySettings - getPasswordComplexitySettings - getLoginSettings ... - Updated default theme colors from blue to a more neutral black and white # Additional changes - Updated login docker image to node 24 --------- Co-authored-by: Ramon <[email protected]>
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import { DynamicTheme } from "@/components/dynamic-theme";
|
||||
import { IdpProcessHandler } from "@/components/idp-process-handler";
|
||||
import { getServiceConfig } from "@/lib/service-url";
|
||||
import { getBrandingSettings, getDefaultOrg } from "@/lib/zitadel";
|
||||
import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
/**
|
||||
* This page handles the initial IDP callback with the single-use token.
|
||||
@@ -20,17 +25,32 @@ export default async function ProcessPage(props: {
|
||||
throw new Error("Missing required IDP callback parameters");
|
||||
}
|
||||
|
||||
const _headers = await headers();
|
||||
const { serviceConfig } = getServiceConfig(_headers);
|
||||
|
||||
let defaultOrganization;
|
||||
if (!organization) {
|
||||
const org: Organization | null = await getDefaultOrg({ serviceConfig });
|
||||
if (org) {
|
||||
defaultOrganization = org.id;
|
||||
}
|
||||
}
|
||||
|
||||
const branding = await getBrandingSettings({ serviceConfig, organization: organization ?? defaultOrganization });
|
||||
|
||||
return (
|
||||
<IdpProcessHandler
|
||||
provider={provider}
|
||||
id={id}
|
||||
token={token}
|
||||
requestId={requestId}
|
||||
organization={organization}
|
||||
link={link}
|
||||
sessionId={linkToSessionId}
|
||||
linkFingerprint={linkFingerprint}
|
||||
postErrorRedirectUrl={postErrorRedirectUrl}
|
||||
/>
|
||||
<DynamicTheme branding={branding}>
|
||||
<IdpProcessHandler
|
||||
provider={provider}
|
||||
id={id}
|
||||
token={token}
|
||||
requestId={requestId}
|
||||
organization={organization}
|
||||
link={link}
|
||||
sessionId={linkToSessionId}
|
||||
linkFingerprint={linkFingerprint}
|
||||
postErrorRedirectUrl={postErrorRedirectUrl}
|
||||
/>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = false;
|
||||
export const fetchCache = "default-no-store";
|
||||
// Add this to prevent RSC requests
|
||||
export const runtime = "nodejs";
|
||||
|
||||
async function loadSessions({ serviceConfig, ids }: { serviceConfig: ServiceConfig; ids: string[] }): Promise<Session[]> {
|
||||
const response = await listSessions({ serviceConfig, ids: ids.filter((id: string | undefined) => !!id) });
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
import { metrics, SpanStatusCode, trace } from "@opentelemetry/api";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
const logger = createLogger("otel-test");
|
||||
|
||||
export async function GET() {
|
||||
|
||||
@@ -94,7 +94,7 @@ export function IdpProcessHandler({
|
||||
}, [provider, id, token, requestId, organization, link, sessionId, linkFingerprint, postErrorRedirectUrl, router, t]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<div className="flex items-center justify-center">
|
||||
{samlData && <AutoSubmitForm url={samlData.url} fields={samlData.fields} />}
|
||||
{loading && (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
|
||||
@@ -30,13 +30,13 @@ export type ColorMap = {
|
||||
[_key in MapName]: Color[];
|
||||
};
|
||||
|
||||
export const DARK_PRIMARY = "#2073c4";
|
||||
export const DARK_PRIMARY = "#eeeeee";
|
||||
export const PRIMARY = "#5469d4";
|
||||
|
||||
export const DARK_WARN = "#ff3b5b";
|
||||
export const WARN = "#cd3d56";
|
||||
|
||||
export const DARK_BACKGROUND = "#111827";
|
||||
export const DARK_BACKGROUND = "#252526";
|
||||
export const BACKGROUND = "#fafafa";
|
||||
|
||||
export const DARK_TEXT = "#ffffff";
|
||||
|
||||
@@ -78,6 +78,12 @@ describe("Theme Configuration", () => {
|
||||
|
||||
describe("getThemeConfig", () => {
|
||||
it("should return default theme when no environment variables are set", () => {
|
||||
delete process.env.NEXT_PUBLIC_THEME_ROUNDNESS;
|
||||
delete process.env.NEXT_PUBLIC_THEME_LAYOUT;
|
||||
delete process.env.NEXT_PUBLIC_THEME_APPEARANCE;
|
||||
delete process.env.NEXT_PUBLIC_THEME_SPACING;
|
||||
delete process.env.NEXT_PUBLIC_THEME_BACKGROUND_IMAGE;
|
||||
|
||||
const config = getThemeConfig();
|
||||
|
||||
expect(config.roundness).toBe(DEFAULT_THEME.roundness);
|
||||
|
||||
+177
-83
@@ -30,7 +30,7 @@ import {
|
||||
VerifyU2FRegistrationRequest,
|
||||
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { unstable_cacheLife as cacheLife } from "next/cache";
|
||||
|
||||
import { getUserAgent } from "./fingerprint";
|
||||
import { createLogger } from "./logger";
|
||||
|
||||
@@ -40,13 +40,55 @@ import { createServiceForHost } from "./service";
|
||||
|
||||
const logger = createLogger("zitadel");
|
||||
|
||||
const useCache = process.env.DEBUG !== "true";
|
||||
const useCache = process.env.API_CACHE_ENABLED !== "false";
|
||||
|
||||
async function cacheWrapper<T>(callback: Promise<T>) {
|
||||
"use cache";
|
||||
cacheLife("hours");
|
||||
let cacheConfig: Record<string, number> = {};
|
||||
try {
|
||||
if (process.env.API_CACHE_CONFIG) {
|
||||
cacheConfig = JSON.parse(process.env.API_CACHE_CONFIG);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to parse API_CACHE_CONFIG", e);
|
||||
}
|
||||
|
||||
return callback;
|
||||
const defaultCacheTTL = (cacheConfig.defaultMinutes ?? 15) * 60 * 1000; // 15 mins default
|
||||
const longCacheTTL = (cacheConfig.longMinutes ?? 60) * 60 * 1000; // 1 hour default
|
||||
|
||||
/**
|
||||
* Helper to determine the TTL for a specific API method.
|
||||
* Allows overriding per-resource via the API_CACHE_CONFIG environment variable.
|
||||
*/
|
||||
function getTTLForKey(keyPrefix: string, fallbackTtl: number) {
|
||||
const configTTL = cacheConfig[keyPrefix];
|
||||
if (typeof configTTL === "number" && !isNaN(configTTL)) {
|
||||
return configTTL * 60 * 1000; // Config expects minutes
|
||||
}
|
||||
return fallbackTtl;
|
||||
}
|
||||
|
||||
const promiseCache = new Map<string, { promise: Promise<any>; expiresAt: number }>();
|
||||
|
||||
/**
|
||||
* A stale-while-revalidate in-memory cache to keep data fresh and deduplicate concurrent requests.
|
||||
* We cache the Promise, so concurrent requests share the exact same execution.
|
||||
*/
|
||||
function freshCache<T>(key: string, fetcher: () => Promise<T>, ttlMs: number): Promise<T> {
|
||||
if (!useCache) {
|
||||
return fetcher();
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const cached = promiseCache.get(key);
|
||||
if (cached && now < cached.expiresAt) {
|
||||
return cached.promise;
|
||||
}
|
||||
|
||||
const promise = fetcher();
|
||||
promiseCache.set(key, { promise, expiresAt: now + ttlMs });
|
||||
|
||||
promise.catch(() => promiseCache.delete(key));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
export async function getHostedLoginTranslation({
|
||||
@@ -57,29 +99,35 @@ export async function getHostedLoginTranslation({
|
||||
organization?: string;
|
||||
locale?: string;
|
||||
}>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getHostedLoginTranslation(
|
||||
{
|
||||
level: organization
|
||||
? {
|
||||
case: "organizationId",
|
||||
value: organization,
|
||||
}
|
||||
: {
|
||||
case: "instance",
|
||||
value: true,
|
||||
},
|
||||
locale: locale,
|
||||
},
|
||||
{},
|
||||
)
|
||||
.then((resp) => {
|
||||
return resp.translations ? resp.translations : undefined;
|
||||
});
|
||||
return settingsService
|
||||
.getHostedLoginTranslation(
|
||||
{
|
||||
level: organization
|
||||
? {
|
||||
case: "organizationId",
|
||||
value: organization,
|
||||
}
|
||||
: {
|
||||
case: "instance",
|
||||
value: true,
|
||||
},
|
||||
locale: locale,
|
||||
},
|
||||
{},
|
||||
)
|
||||
.then((resp) => {
|
||||
return resp.translations ? resp.translations : undefined;
|
||||
});
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getHostedLoginTranslation-${organization || "instance"}-${locale || "default"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getHostedLoginTranslation", longCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getBrandingSettings({
|
||||
@@ -88,13 +136,19 @@ export async function getBrandingSettings({
|
||||
}: WithServiceConfig<{
|
||||
organization?: string;
|
||||
}>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getBrandingSettings({ ctx: makeReqCtx(organization) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService
|
||||
.getBrandingSettings({ ctx: makeReqCtx(organization) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getBrandingSettings-${organization || "instance"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getBrandingSettings", longCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getLoginSettings({
|
||||
@@ -103,41 +157,61 @@ export async function getLoginSettings({
|
||||
}: WithServiceConfig<{
|
||||
organization?: string;
|
||||
}>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getLoginSettings({ ctx: makeReqCtx(organization) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService
|
||||
.getLoginSettings({ ctx: makeReqCtx(organization) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getLoginSettings-${organization || "instance"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getLoginSettings", defaultCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getSecuritySettings({ serviceConfig }: WithServiceConfig) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService.getSecuritySettings({}).then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService.getSecuritySettings({}).then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(`getSecuritySettings-instance`, fetcher, getTTLForKey("getSecuritySettings", defaultCacheTTL));
|
||||
}
|
||||
|
||||
export async function getLockoutSettings({ serviceConfig, orgId }: WithServiceConfig<{ orgId?: string }>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getLockoutSettings({ ctx: makeReqCtx(orgId) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService
|
||||
.getLockoutSettings({ ctx: makeReqCtx(orgId) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getLockoutSettings-${orgId || "instance"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getLockoutSettings", defaultCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getPasswordExpirySettings({ serviceConfig, orgId }: WithServiceConfig<{ orgId?: string }>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getPasswordExpirySettings({ ctx: makeReqCtx(orgId) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService
|
||||
.getPasswordExpirySettings({ ctx: makeReqCtx(orgId) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getPasswordExpirySettings-${orgId || "instance"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getPasswordExpirySettings", defaultCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function listIDPLinks({ serviceConfig, userId }: WithServiceConfig<{ userId: string }>) {
|
||||
@@ -165,16 +239,18 @@ export async function registerTOTP({ serviceConfig, userId }: WithServiceConfig<
|
||||
}
|
||||
|
||||
export async function getAllowedLanguages({ serviceConfig }: WithServiceConfig) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService.getGeneralSettings({}, {}).then((resp) => {
|
||||
return {
|
||||
allowedLanguages: resp.allowedLanguages,
|
||||
defaultLanguage: resp.defaultLanguage,
|
||||
};
|
||||
});
|
||||
return settingsService.getGeneralSettings({}, {}).then((resp) => {
|
||||
return {
|
||||
allowedLanguages: resp.allowedLanguages,
|
||||
defaultLanguage: resp.defaultLanguage,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(`getGeneralSettings-instance`, fetcher, getTTLForKey("getGeneralSettings", longCacheTTL));
|
||||
}
|
||||
|
||||
export async function getLegalAndSupportSettings({
|
||||
@@ -183,13 +259,19 @@ export async function getLegalAndSupportSettings({
|
||||
}: WithServiceConfig<{
|
||||
organization?: string;
|
||||
}>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getLegalAndSupportSettings({ ctx: makeReqCtx(organization) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService
|
||||
.getLegalAndSupportSettings({ ctx: makeReqCtx(organization) }, {})
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getLegalAndSupportSettings-${organization || "instance"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getLegalAndSupportSettings", longCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function getPasswordComplexitySettings({
|
||||
@@ -198,13 +280,19 @@ export async function getPasswordComplexitySettings({
|
||||
}: WithServiceConfig<{
|
||||
organization?: string;
|
||||
}>) {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const settingsService: Client<typeof SettingsService> = await createServiceForHost(SettingsService, serviceConfig);
|
||||
|
||||
const callback = settingsService
|
||||
.getPasswordComplexitySettings({ ctx: makeReqCtx(organization) })
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
return settingsService
|
||||
.getPasswordComplexitySettings({ ctx: makeReqCtx(organization) })
|
||||
.then((resp) => (resp.settings ? resp.settings : undefined));
|
||||
};
|
||||
|
||||
return useCache ? cacheWrapper(callback) : callback;
|
||||
return freshCache(
|
||||
`getPasswordComplexitySettings-${organization || "instance"}`,
|
||||
fetcher,
|
||||
getTTLForKey("getPasswordComplexitySettings", defaultCacheTTL),
|
||||
);
|
||||
}
|
||||
|
||||
export async function createSessionFromChecksAndChallenges({
|
||||
@@ -740,23 +828,29 @@ export async function searchUsers({
|
||||
}
|
||||
|
||||
export async function getDefaultOrg({ serviceConfig }: WithServiceConfig): Promise<Organization | null> {
|
||||
const orgService: Client<typeof OrganizationService> = await createServiceForHost(OrganizationService, serviceConfig);
|
||||
const fetcher = async () => {
|
||||
const orgService: Client<typeof OrganizationService> = await createServiceForHost(OrganizationService, serviceConfig);
|
||||
|
||||
return orgService
|
||||
.listOrganizations(
|
||||
{
|
||||
queries: [
|
||||
{
|
||||
query: {
|
||||
case: "defaultQuery",
|
||||
value: {},
|
||||
return orgService
|
||||
.listOrganizations(
|
||||
{
|
||||
queries: [
|
||||
{
|
||||
query: {
|
||||
case: "defaultQuery",
|
||||
value: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{},
|
||||
)
|
||||
.then((resp) => (resp?.result && resp.result[0] ? resp.result[0] : null));
|
||||
],
|
||||
},
|
||||
{},
|
||||
)
|
||||
.then((resp) => (resp?.result && resp.result[0] ? resp.result[0] : null));
|
||||
};
|
||||
|
||||
return useCache
|
||||
? freshCache(`getDefaultOrg-${"instance"}`, fetcher, getTTLForKey("getDefaultOrg", defaultCacheTTL))
|
||||
: fetcher();
|
||||
}
|
||||
|
||||
export async function getOrgsByDomain({ serviceConfig, domain }: WithServiceConfig<{ domain: string }>) {
|
||||
|
||||
@@ -6,8 +6,6 @@ import { buildCSP } from "./lib/csp";
|
||||
import { createLogger } from "./lib/logger";
|
||||
import { getServiceConfig } from "./lib/service-url";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const logger = createLogger("middleware");
|
||||
export const config = {
|
||||
matcher: ["/.well-known/:path*", "/oauth/:path*", "/oidc/:path*", "/idps/callback/:path*", "/saml/:path*", "/:path*"],
|
||||
@@ -31,7 +29,7 @@ async function loadSecuritySettings(request: NextRequest): Promise<SecuritySetti
|
||||
return response.settings;
|
||||
}
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
export async function proxy(request: NextRequest) {
|
||||
// Add the original URL as a header to all requests
|
||||
const requestHeaders = new Headers(request.headers);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// include styles from the ui package
|
||||
@use "./vars.scss";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
:root {
|
||||
--theme-dark-primary-50: #f1f7fd;
|
||||
--theme-dark-primary-contrast-50: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-100: #afd1f2;
|
||||
--theme-dark-primary-contrast-100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-200: #7fb5ea;
|
||||
--theme-dark-primary-contrast-200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-300: #4192e0;
|
||||
--theme-dark-primary-contrast-300: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-400: #2782dc;
|
||||
--theme-dark-primary-contrast-400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-500: #2073c4;
|
||||
--theme-dark-primary-contrast-500: #ffffff;
|
||||
--theme-dark-primary-600: #1c64aa;
|
||||
--theme-dark-primary-contrast-600: #ffffff;
|
||||
--theme-dark-primary-700: #17548f;
|
||||
--theme-dark-primary-contrast-700: #ffffff;
|
||||
--theme-dark-primary-800: #134575;
|
||||
--theme-dark-primary-contrast-800: #ffffff;
|
||||
--theme-dark-primary-900: #0f355b;
|
||||
--theme-dark-primary-contrast-900: #ffffff;
|
||||
--theme-dark-primary-A100: #e4f2ff;
|
||||
--theme-dark-primary-contrast-A100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-A200: #7ebfff;
|
||||
--theme-dark-primary-contrast-A200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-A400: #278df0;
|
||||
--theme-dark-primary-contrast-A400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-primary-A700: #1d80e0;
|
||||
--theme-dark-primary-contrast-A700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-50: #ffffff;
|
||||
--theme-light-primary-contrast-50: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-100: #ebedfa;
|
||||
--theme-light-primary-contrast-100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-200: #bec6ef;
|
||||
--theme-light-primary-contrast-200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-300: #8594e0;
|
||||
--theme-light-primary-contrast-300: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-400: #6c7eda;
|
||||
--theme-light-primary-contrast-400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-500: #5469d4;
|
||||
--theme-light-primary-contrast-500: #ffffff;
|
||||
--theme-light-primary-600: #3c54ce;
|
||||
--theme-light-primary-contrast-600: #ffffff;
|
||||
--theme-light-primary-700: #2f46bc;
|
||||
--theme-light-primary-contrast-700: #ffffff;
|
||||
--theme-light-primary-800: #293da3;
|
||||
--theme-light-primary-contrast-800: #ffffff;
|
||||
--theme-light-primary-900: #23348b;
|
||||
--theme-light-primary-contrast-900: #ffffff;
|
||||
--theme-light-primary-A100: #ffffff;
|
||||
--theme-light-primary-contrast-A100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-A200: #c5cefc;
|
||||
--theme-light-primary-contrast-A200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-A400: #7085ea;
|
||||
--theme-light-primary-contrast-A400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-primary-A700: #6478de;
|
||||
--theme-light-primary-contrast-A700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-50: #ffffff;
|
||||
--theme-dark-warn-contrast-50: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-100: #fff8f9;
|
||||
--theme-dark-warn-contrast-100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-200: #ffc0ca;
|
||||
--theme-dark-warn-contrast-200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-300: #ff788e;
|
||||
--theme-dark-warn-contrast-300: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-400: #ff5a75;
|
||||
--theme-dark-warn-contrast-400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-500: #ff3b5b;
|
||||
--theme-dark-warn-contrast-500: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-600: #ff1c41;
|
||||
--theme-dark-warn-contrast-600: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-700: #fd0029;
|
||||
--theme-dark-warn-contrast-700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-800: #de0024;
|
||||
--theme-dark-warn-contrast-800: #ffffff;
|
||||
--theme-dark-warn-900: #c0001f;
|
||||
--theme-dark-warn-contrast-900: #ffffff;
|
||||
--theme-dark-warn-A100: #ffffff;
|
||||
--theme-dark-warn-contrast-A100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-A200: #ffd4db;
|
||||
--theme-dark-warn-contrast-A200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-A400: #ff6e86;
|
||||
--theme-dark-warn-contrast-A400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-warn-A700: #ff5470;
|
||||
--theme-dark-warn-contrast-A700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-50: #ffffff;
|
||||
--theme-light-warn-contrast-50: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-100: #f4d3d9;
|
||||
--theme-light-warn-contrast-100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-200: #e8a6b2;
|
||||
--theme-light-warn-contrast-200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-300: #da6e80;
|
||||
--theme-light-warn-contrast-300: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-400: #d3556b;
|
||||
--theme-light-warn-contrast-400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-500: #cd3d56;
|
||||
--theme-light-warn-contrast-500: #ffffff;
|
||||
--theme-light-warn-600: #bb3048;
|
||||
--theme-light-warn-contrast-600: #ffffff;
|
||||
--theme-light-warn-700: #a32a3f;
|
||||
--theme-light-warn-contrast-700: #ffffff;
|
||||
--theme-light-warn-800: #8a2436;
|
||||
--theme-light-warn-contrast-800: #ffffff;
|
||||
--theme-light-warn-900: #721d2c;
|
||||
--theme-light-warn-contrast-900: #ffffff;
|
||||
--theme-light-warn-A100: #ffffff;
|
||||
--theme-light-warn-contrast-A100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-A200: #faa9b7;
|
||||
--theme-light-warn-contrast-A200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-A400: #e65770;
|
||||
--theme-light-warn-contrast-A400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-warn-A700: #d84c64;
|
||||
--theme-light-warn-contrast-A700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-background-50: #7c93c6;
|
||||
--theme-dark-background-contrast-50: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-background-100: #4a69aa;
|
||||
--theme-dark-background-contrast-100: #ffffff;
|
||||
--theme-dark-background-200: #395183;
|
||||
--theme-dark-background-contrast-200: #ffffff;
|
||||
--theme-dark-background-300: #243252;
|
||||
--theme-dark-background-contrast-300: #ffffff;
|
||||
--theme-dark-background-400: #1a253c;
|
||||
--theme-dark-background-contrast-400: #ffffff;
|
||||
--theme-dark-background-500: #111827;
|
||||
--theme-dark-background-contrast-500: #ffffff;
|
||||
--theme-dark-background-600: #080b12;
|
||||
--theme-dark-background-contrast-600: #ffffff;
|
||||
--theme-dark-background-700: #000000;
|
||||
--theme-dark-background-contrast-700: #ffffff;
|
||||
--theme-dark-background-800: #000000;
|
||||
--theme-dark-background-contrast-800: #ffffff;
|
||||
--theme-dark-background-900: #000000;
|
||||
--theme-dark-background-contrast-900: #ffffff;
|
||||
--theme-dark-background-A100: #5782e0;
|
||||
--theme-dark-background-contrast-A100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-background-A200: #204eb1;
|
||||
--theme-dark-background-contrast-A200: #ffffff;
|
||||
--theme-dark-background-A400: #182b53;
|
||||
--theme-dark-background-contrast-A400: #ffffff;
|
||||
--theme-dark-background-A700: #17223b;
|
||||
--theme-dark-background-contrast-A700: #ffffff;
|
||||
--theme-light-background-50: #ffffff;
|
||||
--theme-light-background-contrast-50: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-100: #ffffff;
|
||||
--theme-light-background-contrast-100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-200: #ffffff;
|
||||
--theme-light-background-contrast-200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-300: #ffffff;
|
||||
--theme-light-background-contrast-300: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-400: #ffffff;
|
||||
--theme-light-background-contrast-400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-500: #fafafa;
|
||||
--theme-light-background-contrast-500: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-600: #ebebeb;
|
||||
--theme-light-background-contrast-600: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-700: #dbdbdb;
|
||||
--theme-light-background-contrast-700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-800: #cccccc;
|
||||
--theme-light-background-contrast-800: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-900: #bdbdbd;
|
||||
--theme-light-background-contrast-900: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-A100: #ffffff;
|
||||
--theme-light-background-contrast-A100: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-A200: #ffffff;
|
||||
--theme-light-background-contrast-A200: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-A400: #ffffff;
|
||||
--theme-light-background-contrast-A400: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-light-background-A700: #ffffff;
|
||||
--theme-light-background-contrast-A700: hsla(0, 0%, 0%, 0.87);
|
||||
--theme-dark-text: #ffffff;
|
||||
--theme-dark-secondary-text: #ffffffc7;
|
||||
--theme-light-text: #000000;
|
||||
--theme-light-secondary-text: #000000c7;
|
||||
}
|
||||
Reference in New Issue
Block a user