新增真实账户数据驱动的身份档案卡
个人资料改为“身份卡 + 编辑工作区”双栏布局 侧栏加入语义图标、产品域标题和更明确的选中状态 页面首屏增加身份控制台层级标识 用户名、邮箱、姓名、语言会随表单内容实时呈现 390px 移动端自动堆叠,无横向溢出 相关组件已由 Keycloakify 正式认领,后续同步不会覆盖
This commit is contained in:
+3
-3
@@ -7,6 +7,8 @@
|
||||
# admin/index.css
|
||||
# admin/KcAdminUi.tsx
|
||||
# account/assets/logo.svg
|
||||
# account/personal-info/PersonalInfo.tsx
|
||||
# account/root/PageNav.tsx
|
||||
# admin/assets/logo.svg
|
||||
# admin/i18n/messages_en.properties
|
||||
# admin/i18n/messages_zh_Hans.properties
|
||||
@@ -15,6 +17,7 @@
|
||||
# email/messages/messages_en.properties
|
||||
# email/messages/messages_zh_CN.properties
|
||||
# email/messages/messages_zh_TW.properties
|
||||
# account/components/page/Page.tsx
|
||||
# === Owned files end =====
|
||||
|
||||
# === @keycloakify/email-native v260007.0.0 ===
|
||||
@@ -904,7 +907,6 @@
|
||||
/account/i18n/messages_zh_Hans.properties
|
||||
/account/i18n/messages_zh_Hant.properties
|
||||
/account/organizations/Organizations.tsx
|
||||
/account/personal-info/PersonalInfo.tsx
|
||||
/account/resources/EditTheResource.tsx
|
||||
/account/resources/PermissionRequest.tsx
|
||||
/account/resources/Resources.tsx
|
||||
@@ -914,7 +916,6 @@
|
||||
/account/resources/ShareTheResource.tsx
|
||||
/account/root/header.module.css
|
||||
/account/root/Header.tsx
|
||||
/account/root/PageNav.tsx
|
||||
/account/root/Root.tsx
|
||||
/account/utils/formatDate.ts
|
||||
/account/utils/joinPath.ts
|
||||
@@ -1074,4 +1075,3 @@
|
||||
/account/assets/passkeys/zoho-light.svg
|
||||
/account/assets/passkeys/ztpass-light.png
|
||||
/account/components/datalist/EmptyRow.tsx
|
||||
/account/components/page/Page.tsx
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* This file has been claimed for ownership from @keycloakify/keycloak-account-ui version 260700.0.3.
|
||||
* To relinquish ownership and restore this file to its original content, run the following command:
|
||||
*
|
||||
* $ npx keycloakify own --path "account/components/page/Page.tsx" --revert
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
import {
|
||||
PageSection,
|
||||
Text,
|
||||
TextContent,
|
||||
Title
|
||||
} from "../../../shared/@patternfly/react-core";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
type PageProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export const Page = ({ title, description, children }: PropsWithChildren<PageProps>) => {
|
||||
return (
|
||||
<>
|
||||
<PageSection variant="light">
|
||||
<div className="nexus-page-intro">
|
||||
<div className="nexus-page-intro__copy">
|
||||
<span className="nexus-page-intro__kicker">
|
||||
NEXUS / Identity control
|
||||
</span>
|
||||
<TextContent>
|
||||
<Title headingLevel="h1" data-testid="page-heading">
|
||||
{title}
|
||||
</Title>
|
||||
<Text component="p">{description}</Text>
|
||||
</TextContent>
|
||||
</div>
|
||||
<div className="nexus-page-intro__mark" aria-hidden="true">
|
||||
<span>ID</span>
|
||||
<i />
|
||||
<span>ACCOUNT</span>
|
||||
</div>
|
||||
</div>
|
||||
</PageSection>
|
||||
<PageSection variant="light">{children}</PageSection>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* This file has been claimed for ownership from @keycloakify/keycloak-account-ui version 260700.0.3.
|
||||
* To relinquish ownership and restore this file to its original content, run the following command:
|
||||
*
|
||||
* $ npx keycloakify own --path "account/personal-info/PersonalInfo.tsx" --revert
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
import {
|
||||
UserProfileFields,
|
||||
beerify,
|
||||
debeerify,
|
||||
setUserProfileServerError,
|
||||
useEnvironment
|
||||
} from "../../shared/keycloak-ui-shared";
|
||||
import {
|
||||
ActionGroup,
|
||||
Alert,
|
||||
AlertVariant,
|
||||
Button,
|
||||
ExpandableSection,
|
||||
Form,
|
||||
Spinner
|
||||
} from "../../shared/@patternfly/react-core";
|
||||
import { ExternalLinkSquareAltIcon } from "../../shared/@patternfly/react-icons";
|
||||
import { TFunction } from "i18next";
|
||||
import { useState } from "react";
|
||||
import { ErrorOption, useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getPersonalInfo, getSupportedLocales, savePersonalInfo } from "../api/methods";
|
||||
import { UserProfileMetadata, UserRepresentation } from "../api/representations";
|
||||
import { Page } from "../components/page/Page";
|
||||
import { type AccountEnvironment } from "..";
|
||||
import type { TFuncKey } from "../i18n-type";
|
||||
import { useAccountAlerts } from "../utils/useAccountAlerts";
|
||||
import { usePromise } from "../utils/usePromise";
|
||||
|
||||
export const PersonalInfo = () => {
|
||||
const { t } = useTranslation();
|
||||
const context = useEnvironment<AccountEnvironment>();
|
||||
const [userProfileMetadata, setUserProfileMetadata] = useState<UserProfileMetadata>();
|
||||
const [supportedLocales, setSupportedLocales] = useState<string[]>([]);
|
||||
const form = useForm<UserRepresentation>({ mode: "onChange" });
|
||||
const { handleSubmit, reset, setValue, setError } = form;
|
||||
const profile = form.watch();
|
||||
const { addAlert } = useAccountAlerts();
|
||||
|
||||
usePromise(
|
||||
signal =>
|
||||
Promise.all([
|
||||
getPersonalInfo({ signal, context }),
|
||||
getSupportedLocales({ signal, context })
|
||||
]),
|
||||
([personalInfo, supportedLocales]) => {
|
||||
setUserProfileMetadata(personalInfo.userProfileMetadata);
|
||||
setSupportedLocales(supportedLocales);
|
||||
reset(personalInfo);
|
||||
Object.entries(personalInfo.attributes || {}).forEach(([k, v]) =>
|
||||
setValue(`attributes[${beerify(k)}]`, v)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const onSubmit = async (user: UserRepresentation) => {
|
||||
try {
|
||||
const attributes = Object.fromEntries(
|
||||
Object.entries(user.attributes || {}).map(([k, v]) => [debeerify(k), v])
|
||||
);
|
||||
await savePersonalInfo(context, { ...user, attributes });
|
||||
const locale = attributes["locale"]?.toString();
|
||||
if (locale) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("languageChanged", { detail: { language: locale } })
|
||||
);
|
||||
}
|
||||
await context.keycloak.updateToken();
|
||||
addAlert(t("accountUpdatedMessage"));
|
||||
} catch (error) {
|
||||
addAlert(t("accountUpdatedError"), AlertVariant.danger);
|
||||
|
||||
setUserProfileServerError(
|
||||
{ responseData: { errors: error as any } },
|
||||
(name: string | number, error: unknown) =>
|
||||
setError(name as string, error as ErrorOption),
|
||||
((key: TFuncKey, param?: object) => t(key, param as any)) as TFunction
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (!userProfileMetadata) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const allFieldsReadOnly = () =>
|
||||
userProfileMetadata.attributes
|
||||
.map(a => a.readOnly)
|
||||
.reduce((p, c) => p && c, true);
|
||||
|
||||
const {
|
||||
updateEmailFeatureEnabled,
|
||||
updateEmailActionEnabled,
|
||||
isRegistrationEmailAsUsername,
|
||||
isEditUserNameAllowed
|
||||
} = context.environment.features;
|
||||
|
||||
const displayName =
|
||||
[profile.firstName, profile.lastName].filter(Boolean).join(" ").trim() ||
|
||||
profile.username ||
|
||||
t("personalInfo");
|
||||
const initials =
|
||||
[profile.firstName, profile.lastName]
|
||||
.filter(Boolean)
|
||||
.map(value => value!.trim().charAt(0))
|
||||
.join("")
|
||||
.slice(0, 2)
|
||||
.toUpperCase() || displayName.slice(0, 2).toUpperCase();
|
||||
const profileLocale =
|
||||
profile.attributes?.locale?.toString() || context.environment.locale;
|
||||
|
||||
return (
|
||||
<Page title={t("personalInfo")} description={t("personalInfoDescription")}>
|
||||
<div className="nexus-profile-layout">
|
||||
<aside className="nexus-identity-card" aria-label={t("personalInfo")}>
|
||||
<div className="nexus-identity-card__topline">
|
||||
<span>Identity record</span>
|
||||
<i aria-hidden="true" />
|
||||
</div>
|
||||
<div className="nexus-identity-card__avatar" aria-hidden="true">
|
||||
<span>{initials}</span>
|
||||
</div>
|
||||
<div className="nexus-identity-card__name">
|
||||
<span>{t("personalInfo")}</span>
|
||||
<h2>{displayName}</h2>
|
||||
<p>{profile.email || profile.username}</p>
|
||||
</div>
|
||||
<dl className="nexus-identity-card__facts">
|
||||
<div>
|
||||
<dt>{t("username")}</dt>
|
||||
<dd>{profile.username || "—"}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{t("email")}</dt>
|
||||
<dd>{profile.email || "—"}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Locale</dt>
|
||||
<dd>{profileLocale}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div className="nexus-identity-card__footer">
|
||||
<span aria-hidden="true" />
|
||||
NEXUS identity directory
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section className="nexus-profile-editor">
|
||||
<header className="nexus-profile-editor__header">
|
||||
<span>Profile data</span>
|
||||
<div>
|
||||
<h2>{t("personalInfo")}</h2>
|
||||
<p>{t("personalInfoDescription")}</p>
|
||||
</div>
|
||||
</header>
|
||||
<Form isHorizontal onSubmit={handleSubmit(onSubmit)}>
|
||||
<UserProfileFields
|
||||
form={form}
|
||||
userProfileMetadata={userProfileMetadata}
|
||||
supportedLocales={supportedLocales}
|
||||
currentLocale={context.environment.locale}
|
||||
t={
|
||||
((key: unknown, params) =>
|
||||
t(key as TFuncKey, params as any)) as TFunction
|
||||
}
|
||||
renderer={attribute => {
|
||||
const annotations = attribute.annotations
|
||||
? attribute.annotations
|
||||
: {};
|
||||
return attribute.name === "email" &&
|
||||
updateEmailFeatureEnabled &&
|
||||
updateEmailActionEnabled &&
|
||||
annotations["kc.required.action.supported"] &&
|
||||
(!isRegistrationEmailAsUsername ||
|
||||
isEditUserNameAllowed) ? (
|
||||
<Button
|
||||
id="update-email-btn"
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
context.keycloak.login({
|
||||
action: "UPDATE_EMAIL"
|
||||
})
|
||||
}
|
||||
icon={<ExternalLinkSquareAltIcon />}
|
||||
iconPosition="right"
|
||||
>
|
||||
{t("updateEmail")}
|
||||
</Button>
|
||||
) : undefined;
|
||||
}}
|
||||
/>
|
||||
{!allFieldsReadOnly() && (
|
||||
<ActionGroup>
|
||||
<Button
|
||||
data-testid="save"
|
||||
type="submit"
|
||||
id="save-btn"
|
||||
variant="primary"
|
||||
>
|
||||
{t("save")}
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="cancel"
|
||||
id="cancel-btn"
|
||||
variant="link"
|
||||
onClick={() => reset()}
|
||||
>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
)}
|
||||
{context.environment.features.deleteAccountAllowed && (
|
||||
<ExpandableSection
|
||||
data-testid="delete-account"
|
||||
toggleText={t("deleteAccount")}
|
||||
>
|
||||
<Alert
|
||||
isInline
|
||||
title={t("deleteAccount")}
|
||||
variant="danger"
|
||||
actionLinks={
|
||||
<Button
|
||||
id="delete-account-btn"
|
||||
variant="danger"
|
||||
onClick={() =>
|
||||
context.keycloak.login({
|
||||
action: "delete_account"
|
||||
})
|
||||
}
|
||||
className="delete-button"
|
||||
>
|
||||
{t("delete")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{t("deleteAccountWarning")}
|
||||
</Alert>
|
||||
</ExpandableSection>
|
||||
)}
|
||||
</Form>
|
||||
</section>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalInfo;
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* This file has been claimed for ownership from @keycloakify/keycloak-account-ui version 260700.0.3.
|
||||
* To relinquish ownership and restore this file to its original content, run the following command:
|
||||
*
|
||||
* $ npx keycloakify own --path "account/root/PageNav.tsx" --revert
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
import { useEnvironment } from "../../shared/keycloak-ui-shared";
|
||||
import {
|
||||
Nav,
|
||||
NavExpandable,
|
||||
NavItem,
|
||||
NavList,
|
||||
PageSidebar,
|
||||
PageSidebarBody,
|
||||
Spinner
|
||||
} from "../../shared/@patternfly/react-core";
|
||||
import {
|
||||
PropsWithChildren,
|
||||
MouseEvent as ReactMouseEvent,
|
||||
Suspense,
|
||||
useMemo,
|
||||
useState
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { matchPath, useHref, useLinkClickHandler, useLocation } from "react-router-dom";
|
||||
import {
|
||||
ApplicationsIcon,
|
||||
BuildingIcon,
|
||||
DesktopIcon,
|
||||
FolderOpenAltIcon,
|
||||
KeyIcon,
|
||||
LinkIcon,
|
||||
ShieldAltIcon,
|
||||
UserAltIcon,
|
||||
UserFriendsIcon
|
||||
} from "../../shared/@patternfly/react-icons";
|
||||
|
||||
import fetchContentJson from "../content/fetchContent";
|
||||
import type { TFuncKey } from "../i18n-type";
|
||||
import type { AccountEnvironment, Feature } from "..";
|
||||
import { usePromise } from "../utils/usePromise";
|
||||
|
||||
type RootMenuItem = {
|
||||
id?: string;
|
||||
label: TFuncKey;
|
||||
path: string;
|
||||
isVisible?: keyof Feature;
|
||||
modulePath?: string;
|
||||
};
|
||||
|
||||
type MenuItemWithChildren = {
|
||||
label: TFuncKey;
|
||||
children: MenuItem[];
|
||||
isVisible?: keyof Feature;
|
||||
};
|
||||
|
||||
export type MenuItem = RootMenuItem | MenuItemWithChildren;
|
||||
|
||||
export const PageNav = () => {
|
||||
const [menuItems, setMenuItems] = useState<MenuItem[]>();
|
||||
const context = useEnvironment<AccountEnvironment>();
|
||||
|
||||
usePromise(signal => fetchContentJson({ signal, context }), setMenuItems);
|
||||
return (
|
||||
<PageSidebar>
|
||||
<PageSidebarBody>
|
||||
<div className="nexus-nav-context">
|
||||
<span className="nexus-nav-context__eyebrow">NEXUS / ID</span>
|
||||
<strong>Identity control</strong>
|
||||
<span className="nexus-nav-context__rule" aria-hidden="true" />
|
||||
</div>
|
||||
<Nav>
|
||||
<NavList>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
{menuItems
|
||||
?.filter(menuItem =>
|
||||
menuItem.isVisible
|
||||
? context.environment.features[menuItem.isVisible]
|
||||
: true
|
||||
)
|
||||
.map(menuItem => (
|
||||
<NavMenuItem
|
||||
key={menuItem.label as string}
|
||||
menuItem={menuItem}
|
||||
/>
|
||||
))}
|
||||
</Suspense>
|
||||
</NavList>
|
||||
</Nav>
|
||||
</PageSidebarBody>
|
||||
</PageSidebar>
|
||||
);
|
||||
};
|
||||
|
||||
const menuIcons: Record<string, JSX.Element> = {
|
||||
personalInfo: <UserAltIcon />,
|
||||
accountSecurity: <ShieldAltIcon />,
|
||||
signingIn: <KeyIcon />,
|
||||
deviceActivity: <DesktopIcon />,
|
||||
linkedAccounts: <LinkIcon />,
|
||||
applications: <ApplicationsIcon />,
|
||||
verifiableCredentials: <ShieldAltIcon />,
|
||||
groups: <UserFriendsIcon />,
|
||||
organizations: <BuildingIcon />,
|
||||
resources: <FolderOpenAltIcon />
|
||||
};
|
||||
|
||||
const NavLabel = ({ label }: { label: TFuncKey }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<span className="nexus-nav-label">
|
||||
<span className="nexus-nav-label__icon" aria-hidden="true">
|
||||
{menuIcons[label as string] ?? <UserAltIcon />}
|
||||
</span>
|
||||
<span>{t(label)}</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
type NavMenuItemProps = {
|
||||
menuItem: MenuItem;
|
||||
};
|
||||
|
||||
function NavMenuItem({ menuItem }: NavMenuItemProps) {
|
||||
const { t } = useTranslation();
|
||||
const { environment } = useEnvironment<AccountEnvironment>();
|
||||
const { pathname } = useLocation();
|
||||
const isActive = useMemo(
|
||||
() => matchMenuItem(pathname, menuItem, environment.baseUrl),
|
||||
[pathname, menuItem, environment.baseUrl]
|
||||
);
|
||||
|
||||
if ("path" in menuItem) {
|
||||
return (
|
||||
<NavLink path={menuItem.path} isActive={isActive}>
|
||||
<NavLabel label={menuItem.label} />
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavExpandable
|
||||
data-testid={menuItem.label}
|
||||
title={<NavLabel label={menuItem.label} />}
|
||||
isActive={isActive}
|
||||
isExpanded={isActive}
|
||||
>
|
||||
{menuItem.children
|
||||
.filter(menuItem =>
|
||||
menuItem.isVisible ? environment.features[menuItem.isVisible] : true
|
||||
)
|
||||
.map(child => (
|
||||
<NavMenuItem key={child.label as string} menuItem={child} />
|
||||
))}
|
||||
</NavExpandable>
|
||||
);
|
||||
}
|
||||
|
||||
function getFullUrl(path: string, baseUrl: string) {
|
||||
return `${new URL(baseUrl).pathname}${path}`;
|
||||
}
|
||||
|
||||
function matchMenuItem(
|
||||
currentPath: string,
|
||||
menuItem: MenuItem,
|
||||
baseUrl: string
|
||||
): boolean {
|
||||
if ("path" in menuItem) {
|
||||
return !!matchPath(getFullUrl(menuItem.path, baseUrl), currentPath);
|
||||
}
|
||||
|
||||
return menuItem.children.some(child => matchMenuItem(currentPath, child, baseUrl));
|
||||
}
|
||||
|
||||
type NavLinkProps = {
|
||||
path: string;
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
export const NavLink = ({
|
||||
path,
|
||||
isActive,
|
||||
children
|
||||
}: PropsWithChildren<NavLinkProps>) => {
|
||||
const { environment } = useEnvironment<AccountEnvironment>();
|
||||
const menuItemPath = getFullUrl(path, environment.baseUrl) + location.search;
|
||||
const href = useHref(menuItemPath);
|
||||
const handleClick = useLinkClickHandler(menuItemPath);
|
||||
|
||||
return (
|
||||
<NavItem
|
||||
data-testid={path}
|
||||
to={href}
|
||||
isActive={isActive}
|
||||
onClick={event =>
|
||||
// PatternFly does not have the correct type for this event, so we need to cast it.
|
||||
handleClick(event as unknown as ReactMouseEvent<HTMLAnchorElement>)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</NavItem>
|
||||
);
|
||||
};
|
||||
+793
-138
File diff suppressed because it is too large
Load Diff
+448
-121
@@ -1,45 +1,57 @@
|
||||
:root {
|
||||
--nexus-admin-void: #070b16;
|
||||
--nexus-admin-chrome: #0b1120;
|
||||
--nexus-admin-void: #07101e;
|
||||
--nexus-admin-chrome: #0b1728;
|
||||
--nexus-admin-panel: #ffffff;
|
||||
--nexus-admin-canvas: #f3f6fb;
|
||||
--nexus-admin-ice: #2dc7e5;
|
||||
--nexus-admin-violet: #7569f2;
|
||||
--nexus-admin-text: #172033;
|
||||
--nexus-admin-muted: #63708a;
|
||||
--nexus-admin-line: #dce3ef;
|
||||
--pf-v5-global--primary-color--100: #007f9b;
|
||||
--pf-v5-global--primary-color--200: #00677e;
|
||||
--pf-v5-global--link--Color: #00758d;
|
||||
--pf-v5-global--link--Color--hover: #004f62;
|
||||
--nexus-admin-canvas: #f2f5f8;
|
||||
--nexus-admin-canvas-strong: #e8edf2;
|
||||
--nexus-admin-signal: #00a3b4;
|
||||
--nexus-admin-signal-bright: #58d7e3;
|
||||
--nexus-admin-indigo: #5d63d8;
|
||||
--nexus-admin-mint: #8de3cf;
|
||||
--nexus-admin-text: #142033;
|
||||
--nexus-admin-muted: #607087;
|
||||
--nexus-admin-line: #d8e0e8;
|
||||
--nexus-admin-line-strong: #becad6;
|
||||
--nexus-admin-danger: #c33d4d;
|
||||
--nexus-admin-radius-sm: 5px;
|
||||
--nexus-admin-radius-md: 9px;
|
||||
--nexus-admin-shadow: 0 1px 2px rgba(20, 32, 51, 0.04),
|
||||
0 12px 34px rgba(20, 32, 51, 0.07);
|
||||
--pf-v5-global--primary-color--100: #007f8e;
|
||||
--pf-v5-global--primary-color--200: #006773;
|
||||
--pf-v5-global--link--Color: #007985;
|
||||
--pf-v5-global--link--Color--hover: #005963;
|
||||
--pf-v5-global--BackgroundColor--100: var(--nexus-admin-panel);
|
||||
--pf-v5-global--BackgroundColor--150: #f7f9fc;
|
||||
--pf-v5-global--BackgroundColor--150: #f8fafb;
|
||||
--pf-v5-global--BackgroundColor--200: var(--nexus-admin-canvas);
|
||||
--pf-v5-global--Color--100: var(--nexus-admin-text);
|
||||
--pf-v5-global--Color--200: var(--nexus-admin-muted);
|
||||
--pf-v5-global--BorderColor--100: var(--nexus-admin-line);
|
||||
--pf-v5-global--BorderColor--200: #c9d3e2;
|
||||
--pf-v5-global--BorderColor--200: var(--nexus-admin-line-strong);
|
||||
--pf-v5-c-page__main-section--m-light--BackgroundColor: var(--nexus-admin-canvas);
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
.pf-v5-theme-dark {
|
||||
--nexus-admin-panel: #101827;
|
||||
--nexus-admin-canvas: #080d18;
|
||||
--nexus-admin-text: #edf3ff;
|
||||
--nexus-admin-muted: #a6b2c8;
|
||||
--nexus-admin-line: #2d3b52;
|
||||
--pf-v5-global--primary-color--100: #55d9ef;
|
||||
--pf-v5-global--primary-color--200: #82e5f5;
|
||||
--pf-v5-global--link--Color: #67dcef;
|
||||
--pf-v5-global--link--Color--hover: #a6effa;
|
||||
--nexus-admin-panel: #111d2e;
|
||||
--nexus-admin-canvas: #08111f;
|
||||
--nexus-admin-canvas-strong: #0d192a;
|
||||
--nexus-admin-text: #edf4fb;
|
||||
--nexus-admin-muted: #a6b5c8;
|
||||
--nexus-admin-line: #2a3a50;
|
||||
--nexus-admin-line-strong: #40516a;
|
||||
--nexus-admin-shadow: 0 1px 2px rgba(0, 0, 0, 0.18), 0 16px 40px rgba(0, 0, 0, 0.28);
|
||||
--pf-v5-global--primary-color--100: #58d7e3;
|
||||
--pf-v5-global--primary-color--200: #8de7ee;
|
||||
--pf-v5-global--link--Color: #68dbe5;
|
||||
--pf-v5-global--link--Color--hover: #a0edf2;
|
||||
--pf-v5-global--BackgroundColor--100: var(--nexus-admin-panel);
|
||||
--pf-v5-global--BackgroundColor--150: #151f30;
|
||||
--pf-v5-global--BackgroundColor--150: #162338;
|
||||
--pf-v5-global--BackgroundColor--200: var(--nexus-admin-canvas);
|
||||
--pf-v5-global--Color--100: var(--nexus-admin-text);
|
||||
--pf-v5-global--Color--200: var(--nexus-admin-muted);
|
||||
--pf-v5-global--BorderColor--100: var(--nexus-admin-line);
|
||||
--pf-v5-global--BorderColor--200: #3a4a63;
|
||||
--pf-v5-global--BorderColor--200: var(--nexus-admin-line-strong);
|
||||
--pf-v5-c-page__main-section--m-light--BackgroundColor: var(--nexus-admin-canvas);
|
||||
color-scheme: dark;
|
||||
}
|
||||
@@ -51,9 +63,12 @@ body,
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: var(--nexus-admin-text);
|
||||
background: var(--nexus-admin-canvas);
|
||||
font-family: "Segoe UI Variable", "Segoe UI", sans-serif;
|
||||
font-family: "Segoe UI Variable", "Segoe UI", system-ui, sans-serif;
|
||||
font-size: 15px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.pf-v5-c-page,
|
||||
@@ -63,73 +78,195 @@ body {
|
||||
}
|
||||
|
||||
.pf-v5-c-page__main {
|
||||
background-image: linear-gradient(rgba(32, 62, 104, 0.025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(32, 62, 104, 0.025) 1px, transparent 1px);
|
||||
background-size: 44px 44px;
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
background-image: radial-gradient(
|
||||
circle at 94% 9%,
|
||||
color-mix(in srgb, var(--nexus-admin-signal) 8%, transparent) 0,
|
||||
transparent 24rem
|
||||
),
|
||||
linear-gradient(rgba(41, 73, 104, 0.025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(41, 73, 104, 0.025) 1px, transparent 1px);
|
||||
background-size:
|
||||
auto,
|
||||
48px 48px,
|
||||
48px 48px;
|
||||
}
|
||||
|
||||
.pf-v5-c-masthead {
|
||||
border-bottom: 1px solid rgba(99, 230, 255, 0.18);
|
||||
background: linear-gradient(90deg, rgba(99, 230, 255, 0.035), transparent 36%),
|
||||
min-height: 72px;
|
||||
border-bottom: 1px solid rgba(88, 215, 227, 0.2);
|
||||
background: linear-gradient(90deg, rgba(88, 215, 227, 0.06), transparent 32%),
|
||||
var(--nexus-admin-void);
|
||||
box-shadow: 0 8px 28px rgba(5, 9, 21, 0.18);
|
||||
box-shadow: 0 10px 30px rgba(3, 9, 20, 0.22);
|
||||
}
|
||||
|
||||
.pf-v5-c-masthead::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -1px;
|
||||
width: min(36vw, 34rem);
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, var(--nexus-admin-signal-bright));
|
||||
content: "";
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
img.keycloak__pageheader_brand {
|
||||
width: 190px;
|
||||
height: 38px;
|
||||
width: 192px;
|
||||
height: 42px;
|
||||
object-fit: contain;
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
.pf-v5-c-masthead .pf-v5-c-button.pf-m-link,
|
||||
.pf-v5-c-masthead .pf-v5-c-button.pf-m-plain {
|
||||
color: #b9c8da;
|
||||
}
|
||||
|
||||
.pf-v5-c-masthead .pf-v5-c-button:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pf-v5-c-page__sidebar,
|
||||
.pf-v5-c-page__sidebar-body {
|
||||
border-right: 1px solid rgba(117, 105, 242, 0.16);
|
||||
color: #d8e2f5;
|
||||
color: #dce8f5;
|
||||
background: radial-gradient(
|
||||
circle at 20% 88%,
|
||||
rgba(99, 230, 255, 0.07),
|
||||
transparent 14rem
|
||||
circle at 28% 92%,
|
||||
rgba(88, 215, 227, 0.1),
|
||||
transparent 15rem
|
||||
),
|
||||
linear-gradient(165deg, rgba(93, 99, 216, 0.08), transparent 38%),
|
||||
var(--nexus-admin-chrome);
|
||||
}
|
||||
|
||||
.pf-v5-c-page__sidebar {
|
||||
width: 284px;
|
||||
border-right: 1px solid rgba(88, 215, 227, 0.14);
|
||||
box-shadow: 12px 0 32px rgba(5, 13, 25, 0.08);
|
||||
}
|
||||
|
||||
.pf-v5-c-page__sidebar-body {
|
||||
position: relative;
|
||||
padding: 1.25rem 0.9rem 2rem;
|
||||
}
|
||||
|
||||
.pf-v5-c-page__sidebar-body::before {
|
||||
display: block;
|
||||
width: 72px;
|
||||
height: 10px;
|
||||
margin: 0.15rem 0.8rem 1.1rem;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
var(--nexus-admin-signal-bright) 0 2px,
|
||||
transparent 2.5px
|
||||
)
|
||||
left center / 12px 10px repeat-x;
|
||||
content: "";
|
||||
opacity: 0.9;
|
||||
mask-image: linear-gradient(90deg, #000 55%, transparent);
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__section-title {
|
||||
padding-inline: 1rem;
|
||||
color: #d9e5f2;
|
||||
font-family: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__section-title .pf-v5-c-label {
|
||||
margin-top: 0.55rem;
|
||||
border-color: rgba(88, 215, 227, 0.24);
|
||||
color: var(--nexus-admin-signal-bright);
|
||||
background: rgba(88, 215, 227, 0.08);
|
||||
font-family: "Cascadia Code", Consolas, monospace;
|
||||
font-size: 0.62rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__section-title,
|
||||
.pf-v5-c-nav__link,
|
||||
.pf-v5-c-nav__group-title {
|
||||
color: #9fb0c6;
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__group-title {
|
||||
padding: 1.25rem 1rem 0.45rem;
|
||||
font-family: "Cascadia Code", Consolas, monospace;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__list {
|
||||
display: grid;
|
||||
gap: 0.18rem;
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__link {
|
||||
border-radius: 2px;
|
||||
color: #9daac3;
|
||||
font-size: 0.86rem;
|
||||
position: relative;
|
||||
min-height: 42px;
|
||||
padding: 0.68rem 0.9rem 0.68rem 1rem;
|
||||
border-radius: var(--nexus-admin-radius-sm);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 560;
|
||||
transition:
|
||||
color 150ms ease,
|
||||
background-color 150ms ease,
|
||||
transform 150ms ease;
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__link:hover,
|
||||
.pf-v5-c-nav__link:focus {
|
||||
color: #ffffff;
|
||||
background: rgba(99, 230, 255, 0.06);
|
||||
background: rgba(88, 215, 227, 0.07);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__link.pf-m-current,
|
||||
.pf-v5-c-nav__item.pf-m-current > .pf-v5-c-nav__link {
|
||||
color: #ffffff;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(99, 230, 255, 0.15),
|
||||
rgba(117, 105, 242, 0.08)
|
||||
);
|
||||
box-shadow: inset 2px 0 var(--nexus-admin-ice);
|
||||
background: linear-gradient(90deg, rgba(88, 215, 227, 0.16), rgba(93, 99, 216, 0.08));
|
||||
box-shadow:
|
||||
inset 3px 0 var(--nexus-admin-signal-bright),
|
||||
0 6px 18px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.pf-v5-c-nav__link.pf-m-current::after {
|
||||
border-color: transparent;
|
||||
top: 50%;
|
||||
right: 0.85rem;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--nexus-admin-mint);
|
||||
box-shadow: 0 0 0 4px rgba(141, 227, 207, 0.1);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.pf-v5-c-card,
|
||||
.pf-v5-c-modal-box,
|
||||
.pf-v5-c-drawer__panel,
|
||||
.pf-v5-c-popover__content,
|
||||
.pf-v5-c-menu {
|
||||
border: 1px solid var(--nexus-admin-line);
|
||||
border-radius: 3px;
|
||||
background: var(--nexus-admin-panel);
|
||||
box-shadow: 0 15px 42px rgba(27, 43, 72, 0.08);
|
||||
.pf-v5-c-page__main-section,
|
||||
.pf-v5-c-page__main-breadcrumb {
|
||||
padding-inline: clamp(1.25rem, 3vw, 3.5rem);
|
||||
}
|
||||
|
||||
.pf-v5-c-page__main-section > *,
|
||||
.pf-v5-c-page__main-breadcrumb > * {
|
||||
width: 100%;
|
||||
max-width: 1440px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.pf-v5-c-page__main-breadcrumb {
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--nexus-admin-line) 82%, transparent);
|
||||
background: color-mix(in srgb, var(--nexus-admin-panel) 80%, transparent);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.pf-v5-c-page__main-section {
|
||||
padding-top: clamp(1.5rem, 2.5vw, 2.75rem);
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
|
||||
.pf-v5-c-title,
|
||||
@@ -138,115 +275,252 @@ img.keycloak__pageheader_brand {
|
||||
.pf-v5-c-page__main-title {
|
||||
color: var(--nexus-admin-text);
|
||||
font-family: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif;
|
||||
letter-spacing: -0.018em;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.pf-v5-c-page__main-breadcrumb {
|
||||
border-bottom: 1px solid rgba(220, 227, 239, 0.74);
|
||||
background: color-mix(in srgb, var(--nexus-admin-panel) 72%, transparent);
|
||||
backdrop-filter: blur(12px);
|
||||
.pf-v5-c-title.pf-m-2xl,
|
||||
.pf-v5-c-page__main-title {
|
||||
font-size: clamp(2rem, 3vw, 2.8rem);
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
.pf-v5-c-button {
|
||||
border-radius: 2px;
|
||||
font-weight: 620;
|
||||
.pf-v5-c-title.pf-m-2xl::before,
|
||||
.pf-v5-c-page__main-title::before {
|
||||
display: inline-block;
|
||||
width: 0.48rem;
|
||||
height: 0.48rem;
|
||||
margin: 0 0.72rem 0.2rem 0;
|
||||
border: 2px solid var(--nexus-admin-signal);
|
||||
content: "";
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.pf-v5-c-button.pf-m-primary {
|
||||
color: #ffffff;
|
||||
background: linear-gradient(105deg, #007f9b, #087f91 58%, #655bd7);
|
||||
box-shadow: 0 6px 18px rgba(0, 127, 155, 0.16);
|
||||
.pf-v5-c-content > p,
|
||||
.pf-v5-c-helper-text {
|
||||
color: var(--nexus-admin-muted);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.pf-v5-c-button.pf-m-primary:hover {
|
||||
filter: brightness(1.08);
|
||||
}
|
||||
|
||||
.pf-v5-c-form-control,
|
||||
.pf-v5-c-select__toggle,
|
||||
.pf-v5-c-menu-toggle {
|
||||
border-radius: 2px;
|
||||
border-color: var(--pf-v5-global--BorderColor--200);
|
||||
color: var(--nexus-admin-text);
|
||||
.pf-v5-c-card,
|
||||
.pf-v5-c-data-list,
|
||||
.pf-v5-c-modal-box,
|
||||
.pf-v5-c-drawer__panel,
|
||||
.pf-v5-c-popover__content,
|
||||
.pf-v5-c-menu {
|
||||
border: 1px solid var(--nexus-admin-line);
|
||||
border-radius: var(--nexus-admin-radius-md);
|
||||
background: var(--nexus-admin-panel);
|
||||
box-shadow: none;
|
||||
box-shadow: var(--nexus-admin-shadow);
|
||||
}
|
||||
|
||||
.pf-v5-c-form-control:focus-within,
|
||||
.pf-v5-c-menu-toggle:focus {
|
||||
border-color: #008eaa;
|
||||
box-shadow: 0 0 0 3px rgba(45, 199, 229, 0.12);
|
||||
.pf-v5-c-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pf-v5-c-card__title {
|
||||
font-family: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif;
|
||||
font-size: 1.12rem;
|
||||
letter-spacing: -0.012em;
|
||||
}
|
||||
|
||||
.pf-v5-c-table {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--nexus-admin-line);
|
||||
border-radius: var(--nexus-admin-radius-md);
|
||||
background: var(--nexus-admin-panel);
|
||||
box-shadow: 0 8px 28px rgba(20, 32, 51, 0.055);
|
||||
}
|
||||
|
||||
.pf-v5-c-table thead {
|
||||
color: var(--nexus-admin-muted);
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--nexus-admin-panel) 72%,
|
||||
var(--nexus-admin-canvas)
|
||||
var(--nexus-admin-canvas) 64%,
|
||||
var(--nexus-admin-panel)
|
||||
);
|
||||
font-family: "Cascadia Code", Consolas, monospace;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.025em;
|
||||
font-size: 0.69rem;
|
||||
letter-spacing: 0.045em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.pf-v5-c-table tr:hover > * {
|
||||
.pf-v5-c-table tr > * {
|
||||
border-bottom-color: var(--nexus-admin-line);
|
||||
}
|
||||
|
||||
.pf-v5-c-table tbody tr {
|
||||
transition: background-color 140ms ease;
|
||||
}
|
||||
|
||||
.pf-v5-c-table tbody tr:hover > * {
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--nexus-admin-ice) 7%,
|
||||
var(--nexus-admin-signal) 5%,
|
||||
var(--nexus-admin-panel)
|
||||
);
|
||||
}
|
||||
|
||||
.pf-v5-c-toolbar {
|
||||
border-radius: var(--nexus-admin-radius-md);
|
||||
}
|
||||
|
||||
.pf-v5-c-toolbar__content {
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.pf-v5-c-form__label-text {
|
||||
color: var(--nexus-admin-text);
|
||||
font-weight: 620;
|
||||
}
|
||||
|
||||
.pf-v5-c-form__helper-text {
|
||||
color: var(--nexus-admin-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.pf-v5-c-form-control,
|
||||
.pf-v5-c-select__toggle,
|
||||
.pf-v5-c-menu-toggle,
|
||||
.pf-v5-c-number-input,
|
||||
.pf-v5-c-search-input {
|
||||
min-height: 40px;
|
||||
border-color: var(--nexus-admin-line-strong);
|
||||
border-radius: var(--nexus-admin-radius-sm);
|
||||
color: var(--nexus-admin-text);
|
||||
background: var(--nexus-admin-panel);
|
||||
box-shadow: inset 0 1px 1px rgba(20, 32, 51, 0.025);
|
||||
transition:
|
||||
border-color 140ms ease,
|
||||
box-shadow 140ms ease;
|
||||
}
|
||||
|
||||
.pf-v5-c-form-control:hover,
|
||||
.pf-v5-c-menu-toggle:hover,
|
||||
.pf-v5-c-search-input:hover {
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--nexus-admin-signal) 58%,
|
||||
var(--nexus-admin-line)
|
||||
);
|
||||
}
|
||||
|
||||
.pf-v5-c-form-control:focus-within,
|
||||
.pf-v5-c-menu-toggle:focus,
|
||||
.pf-v5-c-menu-toggle.pf-m-expanded,
|
||||
.pf-v5-c-search-input:focus-within {
|
||||
border-color: var(--nexus-admin-signal);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--nexus-admin-signal) 15%, transparent);
|
||||
}
|
||||
|
||||
.pf-v5-c-button {
|
||||
min-height: 38px;
|
||||
border-radius: var(--nexus-admin-radius-sm);
|
||||
font-weight: 650;
|
||||
letter-spacing: 0.005em;
|
||||
transition:
|
||||
transform 140ms ease,
|
||||
box-shadow 140ms ease,
|
||||
filter 140ms ease;
|
||||
}
|
||||
|
||||
.pf-v5-c-button.pf-m-primary {
|
||||
color: #ffffff;
|
||||
background: #007f8e;
|
||||
box-shadow: 0 7px 18px rgba(0, 127, 142, 0.18);
|
||||
}
|
||||
|
||||
.pf-v5-c-button.pf-m-primary:hover {
|
||||
background: #006f7c;
|
||||
box-shadow: 0 9px 22px rgba(0, 127, 142, 0.24);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.pf-v5-c-button.pf-m-secondary {
|
||||
border-color: var(--nexus-admin-line-strong);
|
||||
color: var(--nexus-admin-text);
|
||||
background: var(--nexus-admin-panel);
|
||||
}
|
||||
|
||||
.pf-v5-c-button.pf-m-danger {
|
||||
background: var(--nexus-admin-danger);
|
||||
}
|
||||
|
||||
.pf-v5-c-tabs {
|
||||
border-bottom: 1px solid var(--nexus-admin-line);
|
||||
}
|
||||
|
||||
.pf-v5-c-tabs__link {
|
||||
color: var(--nexus-admin-muted);
|
||||
font-weight: 620;
|
||||
}
|
||||
|
||||
.pf-v5-c-tabs__item.pf-m-current .pf-v5-c-tabs__link {
|
||||
color: var(--nexus-admin-text);
|
||||
}
|
||||
|
||||
.pf-v5-c-tabs__link::after {
|
||||
border-color: var(--nexus-admin-ice);
|
||||
height: 3px;
|
||||
border-color: var(--nexus-admin-signal);
|
||||
}
|
||||
|
||||
.pf-v5-c-label {
|
||||
border-radius: 2px;
|
||||
border-radius: 999px;
|
||||
font-weight: 620;
|
||||
}
|
||||
|
||||
.pf-v5-c-alert {
|
||||
border: 1px solid var(--nexus-admin-line);
|
||||
border-radius: var(--nexus-admin-radius-md);
|
||||
box-shadow: 0 8px 24px rgba(20, 32, 51, 0.06);
|
||||
}
|
||||
|
||||
.pf-v5-c-empty-state {
|
||||
border: 1px dashed var(--nexus-admin-line-strong);
|
||||
border-radius: var(--nexus-admin-radius-md);
|
||||
background: color-mix(in srgb, var(--nexus-admin-panel) 70%, transparent);
|
||||
}
|
||||
|
||||
.pf-v5-c-pagination {
|
||||
color: var(--nexus-admin-muted);
|
||||
}
|
||||
|
||||
.pf-v5-c-pagination .pf-v5-c-menu-toggle,
|
||||
.pf-v5-c-pagination .pf-v5-c-button {
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.pf-v5-c-modal-box {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pf-v5-c-modal-box__header {
|
||||
border-bottom: 1px solid var(--nexus-admin-line);
|
||||
}
|
||||
|
||||
.pf-v5-c-switch__input:checked ~ .pf-v5-c-switch__toggle {
|
||||
background: var(--nexus-admin-signal);
|
||||
}
|
||||
|
||||
.keycloak__form {
|
||||
max-width: 1100px;
|
||||
max-width: 1160px;
|
||||
padding-bottom: var(--pf-v5-global--spacer--4xl);
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
vertical-align: baseline;
|
||||
accent-color: #0087a1;
|
||||
}
|
||||
|
||||
.keycloak__form_actions {
|
||||
position: fixed;
|
||||
position: sticky;
|
||||
z-index: 15;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
margin-left: calc(-1 * var(--pf-v5-global--spacer--lg));
|
||||
padding: var(--pf-v5-global--spacer--md);
|
||||
margin: 2rem calc(-1 * clamp(1.25rem, 3vw, 3.5rem)) -4rem;
|
||||
padding: 0.9rem clamp(1.25rem, 3vw, 3.5rem);
|
||||
border-top: 1px solid var(--nexus-admin-line);
|
||||
background: color-mix(in srgb, var(--nexus-admin-panel) 90%, transparent);
|
||||
box-shadow: 0 -10px 30px rgba(27, 43, 72, 0.06);
|
||||
backdrop-filter: blur(14px);
|
||||
box-shadow: 0 -10px 30px rgba(20, 32, 51, 0.06);
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.pf-v5-theme-dark .pf-v5-c-page__main {
|
||||
background-image: linear-gradient(rgba(99, 230, 255, 0.035) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(99, 230, 255, 0.035) 1px, transparent 1px);
|
||||
}
|
||||
|
||||
.pf-v5-theme-dark .pf-v5-c-card,
|
||||
.pf-v5-theme-dark .pf-v5-c-modal-box,
|
||||
.pf-v5-theme-dark .pf-v5-c-drawer__panel,
|
||||
.pf-v5-theme-dark .pf-v5-c-popover__content,
|
||||
.pf-v5-theme-dark .pf-v5-c-menu {
|
||||
box-shadow: 0 15px 42px rgba(0, 0, 0, 0.28);
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
accent-color: var(--nexus-admin-signal);
|
||||
}
|
||||
|
||||
.keycloak__section_intro__help {
|
||||
@@ -256,19 +530,72 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
div.w-tc-editor {
|
||||
border: 1px solid var(--nexus-admin-line);
|
||||
border-radius: var(--nexus-admin-radius-sm);
|
||||
font-family: "Cascadia Code", "SFMono-Regular", Consolas, monospace;
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--nexus-admin-ice) !important;
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid var(--nexus-admin-signal-bright) !important;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.pf-v5-c-page__main-section > .pf-v5-c-card,
|
||||
.pf-v5-c-page__main-section > .pf-v5-c-table,
|
||||
.pf-v5-c-page__main-section > .pf-v5-c-form {
|
||||
animation: nexus-admin-arrive 360ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes nexus-admin-arrive {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pf-v5-c-masthead {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
img.keycloak__pageheader_brand {
|
||||
width: 158px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.pf-v5-c-page__sidebar {
|
||||
width: min(88vw, 310px);
|
||||
}
|
||||
|
||||
.pf-v5-c-page__main-section,
|
||||
.pf-v5-c-page__main-breadcrumb {
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
.pf-v5-c-title.pf-m-2xl,
|
||||
.pf-v5-c-page__main-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.keycloak__form_actions {
|
||||
margin-inline: -1rem;
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
scroll-behavior: auto !important;
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user