This commit is contained in:
2026-08-03 11:09:19 +08:00 Unverified
parent f00e9035a2
commit 4577e60fc2
15 changed files with 570 additions and 883 deletions
+26
View File
@@ -13,6 +13,7 @@
"@dagrejs/dagre": "^1.1.8", "@dagrejs/dagre": "^1.1.8",
"@keycloak/keycloak-admin-client": "26.7.0", "@keycloak/keycloak-admin-client": "26.7.0",
"@keycloakify/email-native": "~260007.0.0", "@keycloakify/email-native": "~260007.0.0",
"@keycloakify/keycloak-account-ui": "~260700.0.3",
"@keycloakify/keycloak-admin-ui": "~260700.0.2", "@keycloakify/keycloak-admin-ui": "~260700.0.2",
"@keycloakify/keycloak-ui-shared": "~260700.0.2", "@keycloakify/keycloak-ui-shared": "~260700.0.2",
"@patternfly/patternfly": "^5.4.2", "@patternfly/patternfly": "^5.4.2",
@@ -1246,6 +1247,31 @@
"integrity": "sha512-hFUiOwwpc84eXPAct44G/pTBkYnDQF1jIObME+ZUrb7XidmFJ/TdH9q49LKpd9sIivSyOEJa1qLtrggP+c+BJw==", "integrity": "sha512-hFUiOwwpc84eXPAct44G/pTBkYnDQF1jIObME+ZUrb7XidmFJ/TdH9q49LKpd9sIivSyOEJa1qLtrggP+c+BJw==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@keycloakify/keycloak-account-ui": {
"version": "260700.0.3",
"resolved": "https://registry.npmjs.org/@keycloakify/keycloak-account-ui/-/keycloak-account-ui-260700.0.3.tgz",
"integrity": "sha512-K7jveFTb4BYYs33KI5VL+wF4H6/QSmQWT9NnqJsZbdIy7vnaFwZPUGRzJ2giQ9FHxGqG6R63W/mgmrnyzwdDUA==",
"license": "Apache-2.0",
"dependencies": {
"tsafe": "^1.8.5"
},
"peerDependencies": {
"@keycloakify/keycloak-ui-shared": "~260700.0.2",
"@patternfly/patternfly": "^5.4.2",
"@patternfly/react-core": "^5.4.14",
"@patternfly/react-icons": "^5.4.2",
"@patternfly/react-table": "^5.4.16",
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.3.18",
"i18next": "^25.7.3",
"i18next-fetch-backend": "^7.0.0",
"lodash-es": "^4.18.1",
"react": "^18.3.1",
"react-hook-form": "^7.70.0",
"react-i18next": "^16.5.1",
"react-router-dom": "^6.30.4"
}
},
"node_modules/@keycloakify/keycloak-admin-ui": { "node_modules/@keycloakify/keycloak-admin-ui": {
"version": "260700.0.2", "version": "260700.0.2",
"resolved": "https://registry.npmjs.org/@keycloakify/keycloak-admin-ui/-/keycloak-admin-ui-260700.0.2.tgz", "resolved": "https://registry.npmjs.org/@keycloakify/keycloak-admin-ui/-/keycloak-admin-ui-260700.0.2.tgz",
+1
View File
@@ -22,6 +22,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"@keycloakify/keycloak-admin-ui": "~260700.0.2", "@keycloakify/keycloak-admin-ui": "~260700.0.2",
"@keycloakify/keycloak-account-ui": "~260700.0.3",
"@dagrejs/dagre": "^1.1.8", "@dagrejs/dagre": "^1.1.8",
"@keycloak/keycloak-admin-client": "26.7.0", "@keycloak/keycloak-admin-client": "26.7.0",
"@keycloakify/keycloak-ui-shared": "~260700.0.2", "@keycloakify/keycloak-ui-shared": "~260700.0.2",
+3
View File
@@ -5,3 +5,6 @@
# === @keycloakify/keycloak-admin-ui v260700.0.2 === # === @keycloakify/keycloak-admin-ui v260700.0.2 ===
/admin/early-color-scheme.js /admin/early-color-scheme.js
# === @keycloakify/keycloak-account-ui v260700.0.3 ===
/account/early-color-scheme.js
+244
View File
@@ -1,8 +1,12 @@
# This file is managed by Keycloakify, do not edit it manually. # This file is managed by Keycloakify, do not edit it manually.
# === Owned files start === # === Owned files start ===
# account/KcAccountUi.tsx
# account/KcContext.ts
# account/KcPage.tsx
# admin/index.css # admin/index.css
# admin/KcAdminUi.tsx # admin/KcAdminUi.tsx
# account/assets/logo.svg
# admin/assets/logo.svg # admin/assets/logo.svg
# admin/i18n/messages_en.properties # admin/i18n/messages_en.properties
# admin/i18n/messages_zh_Hans.properties # admin/i18n/messages_zh_Hans.properties
@@ -831,3 +835,243 @@
/shared/keycloak-ui-shared/controls/table/ListEmptyState.tsx /shared/keycloak-ui-shared/controls/table/ListEmptyState.tsx
/shared/keycloak-ui-shared/controls/table/PaginatingTableToolbar.tsx /shared/keycloak-ui-shared/controls/table/PaginatingTableToolbar.tsx
/shared/keycloak-ui-shared/controls/table/TableToolbar.tsx /shared/keycloak-ui-shared/controls/table/TableToolbar.tsx
# === @keycloakify/keycloak-account-ui v260700.0.3 ===
/account/api.ts
/account/colorScheme.ts
/account/environment.ts
/account/i18n-type.ts
/account/i18next.d.ts
/account/index.ts
/account/routes.tsx
/account/account-security/AccountRow.tsx
/account/account-security/DeviceActivity.tsx
/account/account-security/LinkedAccounts.tsx
/account/account-security/LinkedAccountsToolbar.tsx
/account/account-security/SigningIn.tsx
/account/api/constants.ts
/account/api/methods.ts
/account/api/parse-links.ts
/account/api/parse-response.ts
/account/api/representations.ts
/account/api/request.ts
/account/applications/Applications.tsx
/account/assets/content.ts
/account/assets/favicon.svg
/account/content/ContentComponent.tsx
/account/content/fetchContent.ts
/account/groups/Groups.tsx
/account/i18n/i18n.ts
/account/i18n/index.ts
/account/i18n/messages_ar.properties
/account/i18n/messages_az.properties
/account/i18n/messages_ca.properties
/account/i18n/messages_cs.properties
/account/i18n/messages_da.properties
/account/i18n/messages_de.properties
/account/i18n/messages_el.properties
/account/i18n/messages_en.properties
/account/i18n/messages_es.properties
/account/i18n/messages_eu.properties
/account/i18n/messages_fa.properties
/account/i18n/messages_fi.properties
/account/i18n/messages_fr.properties
/account/i18n/messages_hr.properties
/account/i18n/messages_hu.properties
/account/i18n/messages_hy.properties
/account/i18n/messages_id.properties
/account/i18n/messages_it.properties
/account/i18n/messages_ja.properties
/account/i18n/messages_ka.properties
/account/i18n/messages_kk.properties
/account/i18n/messages_ky.properties
/account/i18n/messages_lt.properties
/account/i18n/messages_lv.properties
/account/i18n/messages_nl.properties
/account/i18n/messages_no.properties
/account/i18n/messages_pl.properties
/account/i18n/messages_pt_BR.properties
/account/i18n/messages_pt.properties
/account/i18n/messages_ro.properties
/account/i18n/messages_ru.properties
/account/i18n/messages_sk.properties
/account/i18n/messages_sl.properties
/account/i18n/messages_sv.properties
/account/i18n/messages_th.properties
/account/i18n/messages_tr.properties
/account/i18n/messages_uk.properties
/account/i18n/messages_vi.properties
/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
/account/resources/ResourcesTab.tsx
/account/resources/ResourceToolbar.tsx
/account/resources/SharedWith.tsx
/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
/account/utils/useAccountAlerts.ts
/account/utils/usePromise.ts
/account/verifiable-credentials/CredentialRow.tsx
/account/verifiable-credentials/IssuedCredentialsModal.tsx
/account/verifiable-credentials/UserAttributesDialog.tsx
/account/verifiable-credentials/VerifiableCredentials.tsx
/account/assets/passkeys/1password-dark.svg
/account/assets/passkeys/1password-light.svg
/account/assets/passkeys/acs-light.png
/account/assets/passkeys/aliasvault-light.svg
/account/assets/passkeys/allthenticator-light.png
/account/assets/passkeys/android-light.png
/account/assets/passkeys/apple-light.svg
/account/assets/passkeys/arculus-light.png
/account/assets/passkeys/atkey-light.png
/account/assets/passkeys/atkey1-light.png
/account/assets/passkeys/atkey2-light.png
/account/assets/passkeys/atkey4-light.png
/account/assets/passkeys/atlkey-light.png
/account/assets/passkeys/atos-light.png
/account/assets/passkeys/authenton1-light.png
/account/assets/passkeys/bitwarden-light.svg
/account/assets/passkeys/burp-light.svg
/account/assets/passkeys/cardos-light.png
/account/assets/passkeys/chipwon-light.png
/account/assets/passkeys/chrome-light.svg
/account/assets/passkeys/chunghwa-light.png
/account/assets/passkeys/crayonic-light.png
/account/assets/passkeys/cryptnox-light.png
/account/assets/passkeys/dapple-light.png
/account/assets/passkeys/dashlane-dark.svg
/account/assets/passkeys/dashlane-light.svg
/account/assets/passkeys/deepnet-light.png
/account/assets/passkeys/devolutions-light.svg
/account/assets/passkeys/edge-light.svg
/account/assets/passkeys/egomet-light.png
/account/assets/passkeys/ellipticsecure-light.png
/account/assets/passkeys/enpass-dark.svg
/account/assets/passkeys/enpass-light.svg
/account/assets/passkeys/ensurity-light.png
/account/assets/passkeys/ensurity1-light.png
/account/assets/passkeys/ensurity2-light.png
/account/assets/passkeys/ensurity3-light.png
/account/assets/passkeys/enterprise-light.svg
/account/assets/passkeys/ess-light.png
/account/assets/passkeys/ewbm-light.png
/account/assets/passkeys/excelsecu-light.png
/account/assets/passkeys/feitian-light.png
/account/assets/passkeys/fido-light.png
/account/assets/passkeys/fido1-light.png
/account/assets/passkeys/foongtone-light.png
/account/assets/passkeys/goldkey-light.png
/account/assets/passkeys/google-light.png
/account/assets/passkeys/google1-light.svg
/account/assets/passkeys/gotrust-light.png
/account/assets/passkeys/gstag-light.png
/account/assets/passkeys/heimlane-light.svg
/account/assets/passkeys/hid-light.png
/account/assets/passkeys/hideez-light.png
/account/assets/passkeys/hyper-light.png
/account/assets/passkeys/hyper1-light.png
/account/assets/passkeys/hypr-light.png
/account/assets/passkeys/icloud-dark.svg
/account/assets/passkeys/icloud-light.svg
/account/assets/passkeys/id-one-light.png
/account/assets/passkeys/ideem-light.png
/account/assets/passkeys/idemia-light.png
/account/assets/passkeys/idex-light.png
/account/assets/passkeys/idmelon-light.svg
/account/assets/passkeys/idmelon1-light.png
/account/assets/passkeys/iist-light.png
/account/assets/passkeys/initial-light.svg
/account/assets/passkeys/ipasswords-light.svg
/account/assets/passkeys/kaspersky-light.svg
/account/assets/passkeys/keepassdx-dark.svg
/account/assets/passkeys/keepassdx-light.svg
/account/assets/passkeys/keepassxc-dark.svg
/account/assets/passkeys/keepassxc-light.svg
/account/assets/passkeys/keeper-light.svg
/account/assets/passkeys/key-id-light.png
/account/assets/passkeys/keyvault-light.png
/account/assets/passkeys/keyxentic-light.png
/account/assets/passkeys/konai-light.png
/account/assets/passkeys/kqc-light.png
/account/assets/passkeys/lastpass-dark.svg
/account/assets/passkeys/lastpass-light.svg
/account/assets/passkeys/ledger-light.png
/account/assets/passkeys/logmeonce-dark.svg
/account/assets/passkeys/logmeonce-light.svg
/account/assets/passkeys/mettlesemi-light.png
/account/assets/passkeys/microsoft-light.svg
/account/assets/passkeys/neowave-light.png
/account/assets/passkeys/nitrokey-light.png
/account/assets/passkeys/nordpass-dark.svg
/account/assets/passkeys/nordpass-light.svg
/account/assets/passkeys/nxp-light.png
/account/assets/passkeys/nymi-light.png
/account/assets/passkeys/octatco-light.png
/account/assets/passkeys/onekey-light.png
/account/assets/passkeys/onespan-light.png
/account/assets/passkeys/onespan1-light.png
/account/assets/passkeys/onespan2-light.png
/account/assets/passkeys/onespan3-light.png
/account/assets/passkeys/onespan4-light.png
/account/assets/passkeys/onespan5-light.png
/account/assets/passkeys/onespan7-light.png
/account/assets/passkeys/onlykey-light.png
/account/assets/passkeys/opensk-light.png
/account/assets/passkeys/pone-light.png
/account/assets/passkeys/precision-light.png
/account/assets/passkeys/precision1-light.png
/account/assets/passkeys/precision2-light.png
/account/assets/passkeys/proton-light.svg
/account/assets/passkeys/pwsafe-light.svg
/account/assets/passkeys/rsa-light.png
/account/assets/passkeys/samsung-light.svg
/account/assets/passkeys/secora-light.png
/account/assets/passkeys/secora1-light.png
/account/assets/passkeys/securitag-light.png
/account/assets/passkeys/sentry-light.png
/account/assets/passkeys/sesame-light.svg
/account/assets/passkeys/shalo-light.png
/account/assets/passkeys/sherlocked-light.svg
/account/assets/passkeys/smartdisplayer-light.png
/account/assets/passkeys/solo-light.png
/account/assets/passkeys/starsign-light.png
/account/assets/passkeys/sticky-light.svg
/account/assets/passkeys/swissbit-light.png
/account/assets/passkeys/t-shield-light.png
/account/assets/passkeys/taglio-light.png
/account/assets/passkeys/thales-light.png
/account/assets/passkeys/thales1-dark.svg
/account/assets/passkeys/thales1-light.svg
/account/assets/passkeys/thales3-dark.svg
/account/assets/passkeys/thales3-light.svg
/account/assets/passkeys/thetis-light.png
/account/assets/passkeys/token-light.png
/account/assets/passkeys/token2-light.png
/account/assets/passkeys/toothpic-dark.svg
/account/assets/passkeys/toothpic-light.svg
/account/assets/passkeys/truu-light.png
/account/assets/passkeys/utrust-light.png
/account/assets/passkeys/valmido-light.png
/account/assets/passkeys/veridium-light.png
/account/assets/passkeys/verimark-light.png
/account/assets/passkeys/verimark1-light.png
/account/assets/passkeys/verocard-light.png
/account/assets/passkeys/vincss-light.png
/account/assets/passkeys/vivokey-light.png
/account/assets/passkeys/webcomm-light.png
/account/assets/passkeys/windows-light.svg
/account/assets/passkeys/winmagic-light.png
/account/assets/passkeys/yubikey-light.png
/account/assets/passkeys/zoho-light.svg
/account/assets/passkeys/ztpass-light.png
/account/components/datalist/EmptyRow.tsx
/account/components/page/Page.tsx
+42
View File
@@ -0,0 +1,42 @@
/**
* 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/KcAccountUi.tsx" --revert
*/
import "@patternfly/patternfly/patternfly-addons.css";
import "@patternfly/react-core/dist/styles/base.css";
import "./theme.css";
import { useReducer, useEffect } from "react";
import { startColorSchemeManagement } from "./colorScheme";
import { KeycloakProvider } from "../shared/keycloak-ui-shared";
import { environment } from "./environment";
import { i18n } from "./i18n/i18n";
import { Root } from "./root/Root";
import { SessionExpirationWarningOverlay } from "../shared/SessionExpirationWarningOverlay";
document.title = "NEXUS / ID";
const prI18nInitialized = i18n.init();
startColorSchemeManagement();
export default function KcAccountUi() {
const [isI18nInitialized, setI18nInitialized] = useReducer(() => true, false);
useEffect(() => {
prI18nInitialized.then(() => setI18nInitialized());
}, []);
if (!isI18nInitialized) {
return null;
}
return (
<KeycloakProvider environment={environment}>
<Root />
<SessionExpirationWarningOverlay warnUserSecondsBeforeAutoLogout={45} />
</KeycloakProvider>
);
}
+14 -9
View File
@@ -1,12 +1,17 @@
/* eslint-disable @typescript-eslint/ban-types */ /**
import type { ExtendKcContext } from "keycloakify/account"; * This file has been claimed for ownership from @keycloakify/keycloak-account-ui version 260700.0.3.
import type { KcEnvName, ThemeName } from "../kc.gen"; * To relinquish ownership and restore this file to its original content, run the following command:
*
* $ npx keycloakify own --path "account/KcContext.ts" --revert
*/
export type KcContextExtension = { import { type KcContextLike, createGetKcContext } from "@keycloakify/keycloak-account-ui";
themeName: ThemeName; import type { KcEnvName } from "../kc.gen";
properties: Record<KcEnvName, string> & {};
export type KcContext = KcContextLike & {
themeType: "account";
themeName: string;
properties: Record<KcEnvName, string>;
}; };
export type KcContextExtensionPerPage = {}; export const { getKcContext } = createGetKcContext<KcContext>();
export type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;
+27 -40
View File
@@ -1,47 +1,34 @@
import { Suspense } from "react"; /**
import type { ClassKey } from "keycloakify/account"; * 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/KcPage.tsx" --revert
*/
import { lazy } from "react";
import { KcAccountUiLoader } from "@keycloakify/keycloak-account-ui";
import type { KcContext } from "./KcContext"; import type { KcContext } from "./KcContext";
import { useI18n } from "./i18n"; import { oidcEarlyInit } from "oidc-spa/entrypoint";
import DefaultPage from "keycloakify/account/DefaultPage"; import { browserRuntimeFreeze } from "oidc-spa/browser-runtime-freeze";
import Template from "./Template"; import { DPoP } from "oidc-spa/DPoP";
import "./theme.css";
const KcAccountUi = lazy(() => import("./KcAccountUi"));
export default function KcPage(props: { kcContext: KcContext }) { export default function KcPage(props: { kcContext: KcContext }) {
const { kcContext } = props; const { kcContext } = props;
const { i18n } = useI18n({ kcContext }); const { shouldLoadApp } = oidcEarlyInit({
BASE_URL: kcContext.baseUrl.path,
return ( sessionRestorationMethod: import.meta.env.DEV ? "full page redirect" : "auto",
<Suspense> securityDefenses: {
{(() => { ...browserRuntimeFreeze({ excludes: ["fetch"] }),
switch (kcContext.pageId) { ...DPoP({ mode: "auto" })
default:
return (
<DefaultPage
kcContext={kcContext}
i18n={i18n}
classes={classes}
Template={Template}
doUseDefaultCss={false}
/>
);
} }
})()} });
</Suspense>
);
}
const classes = { if (!shouldLoadApp) {
kcHtmlClass: "nexus-account-html", return null;
kcBodyClass: "nexus-account-body", }
kcButtonClass: "nexus-account-button",
kcButtonPrimaryClass: "nexus-account-button--primary", return <KcAccountUiLoader kcContext={kcContext} KcAccountUi={KcAccountUi} />;
kcButtonDefaultClass: "nexus-account-button--default", }
kcButtonLargeClass: "nexus-account-button--large",
kcFormClass: "nexus-account-form",
kcFormGroupClass: "nexus-account-field",
kcLabelClass: "nexus-account-label",
kcInputClass: "nexus-account-input",
kcInputWrapperClass: "nexus-account-input-wrapper",
kcInputErrorMessageClass: "nexus-account-field-error"
} satisfies { [key in ClassKey]?: string };
-38
View File
@@ -1,38 +0,0 @@
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
import type { KcContext } from "./KcContext";
import { createGetKcContextMock } from "keycloakify/account/KcContext";
import type { KcContextExtension, KcContextExtensionPerPage } from "./KcContext";
import KcPage from "./KcPage";
import { themeNames, kcEnvDefaults } from "../kc.gen";
const kcContextExtension: KcContextExtension = {
themeName: themeNames[0],
properties: {
...kcEnvDefaults
}
};
const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
export const { getKcContextMock } = createGetKcContextMock({
kcContextExtension,
kcContextExtensionPerPage,
overrides: {},
overridesPerPage: {}
});
export function createKcPageStory<PageId extends KcContext["pageId"]>(params: { pageId: PageId }) {
const { pageId } = params;
function KcPageStory(props: { kcContext?: DeepPartial<Extract<KcContext, { pageId: PageId }>> }) {
const { kcContext: overrides } = props;
const kcContextMock = getKcContextMock({
pageId,
overrides
});
return <KcPage kcContext={kcContextMock} />;
}
return { KcPageStory };
}
-124
View File
@@ -1,124 +0,0 @@
import { useEffect } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { kcSanitize } from "keycloakify/lib/kcSanitize";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import { useSetClassName } from "keycloakify/tools/useSetClassName";
import { useInitialize } from "keycloakify/account/Template.useInitialize";
import type { TemplateProps } from "keycloakify/account/TemplateProps";
import type { I18n } from "./i18n";
import type { KcContext } from "./KcContext";
export default function Template(props: TemplateProps<KcContext, I18n>) {
const { kcContext, i18n, doUseDefaultCss, active, classes, children } = props;
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const { msg, msgStr, currentLanguage, enabledLanguages } = i18n;
const { url, features, realm, message, referrer } = kcContext;
useEffect(() => {
document.title = msgStr("accountManagementTitle");
}, [msgStr]);
useSetClassName({ qualifiedName: "html", className: kcClsx("kcHtmlClass") });
useSetClassName({ qualifiedName: "body", className: kcClsx("kcBodyClass") });
const { isReadyToRender } = useInitialize({ kcContext, doUseDefaultCss });
if (!isReadyToRender) {
return null;
}
const navigationItems = [
{ id: "account", href: url.accountUrl, label: msg("account"), visible: true },
{ id: "password", href: url.passwordUrl, label: msg("password"), visible: features.passwordUpdateSupported },
{ id: "totp", href: url.totpUrl, label: msg("authenticator"), visible: true },
{ id: "social", href: url.socialUrl, label: msg("federatedIdentity"), visible: features.identityFederation },
{ id: "sessions", href: url.sessionsUrl, label: msg("sessions"), visible: true },
{ id: "applications", href: url.applicationsUrl, label: msg("applications"), visible: true },
{ id: "log", href: url.logUrl, label: msg("log"), visible: features.log },
{
id: "authorization",
href: url.resourceUrl,
label: msg("myResources"),
visible: realm.userManagedAccessAllowed && features.authorization
}
].filter(item => item.visible);
return (
<div className="nexus-account-shell">
<header className="nexus-account-header">
<a className="nexus-account-brand" href={url.accountUrl} aria-label={msgStr("accountManagementTitle")}>
<span className="nexus-account-brand__mark">N</span>
<span>
NEXUS <b>/ ID</b>
</span>
</a>
<div className="nexus-account-header__realm">
<span>{msg("nexusIdentityControl")}</span>
<strong>{msg("accountManagementTitle")}</strong>
</div>
<nav className="nexus-account-utility" aria-label={msgStr("nexusAccountUtilities")}>
{enabledLanguages.length > 1 && (
<details className="nexus-account-language">
<summary>{currentLanguage.label}</summary>
<ul>
{enabledLanguages.map(({ languageTag, label, href }) => (
<li key={languageTag}>
<a href={href}>{label}</a>
</li>
))}
</ul>
</details>
)}
{referrer?.url && (
<a href={referrer.url} id="referrer">
{msg("backTo", referrer.name)}
</a>
)}
<a className="nexus-account-signout" href={url.getLogoutUrl()}>
{msg("doSignOut")}
</a>
</nav>
</header>
<div className="nexus-account-layout">
<aside className="nexus-account-sidebar">
<div className="nexus-account-orbit" aria-hidden="true">
<span />
</div>
<p className="nexus-account-sidebar__label">{msg("nexusAccountMatrix")}</p>
<nav aria-label={msgStr("accountManagementTitle")}>
<ul>
{navigationItems.map(item => (
<li key={item.id} className={clsx(active === item.id && "active")}>
<a href={item.href}>
<span className="nexus-account-nav__signal" />
{item.label}
</a>
</li>
))}
</ul>
</nav>
<div className="nexus-account-sidebar__status">
<span /> {msg("nexusSecureSession")}
</div>
</aside>
<main className="nexus-account-content" id="nexus-account-main">
{message !== undefined && (
<div className={clsx("nexus-account-alert", `nexus-account-alert--${message.type}`)} role="status">
<span className="nexus-account-alert__signal" />
<span
dangerouslySetInnerHTML={{
__html: kcSanitize(message.summary)
}}
/>
</div>
)}
<section className="nexus-account-panel">{children}</section>
</main>
</div>
</div>
);
}
+16
View File
@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 52" role="img" aria-labelledby="title">
<title id="title">NEXUS ID</title>
<defs>
<linearGradient id="signal" x1="0" x2="1" y1="0" y2="1">
<stop offset="0" stop-color="#63e6ff"/>
<stop offset="1" stop-color="#8b7cff"/>
</linearGradient>
</defs>
<g transform="translate(8 9)">
<rect x="5" y="5" width="24" height="24" rx="1" fill="none" stroke="url(#signal)" stroke-width="1.5" transform="rotate(45 17 17)"/>
<circle cx="17" cy="17" r="3.5" fill="#63e6ff"/>
<circle cx="17" cy="17" r="8.5" fill="none" stroke="#63e6ff" stroke-opacity=".18"/>
</g>
<text x="52" y="32" fill="#f4f7ff" font-family="Bahnschrift,Segoe UI,sans-serif" font-size="20" font-weight="600" letter-spacing="3">NEXUS</text>
<text x="151" y="32" fill="#95a2be" font-family="Bahnschrift,Segoe UI,sans-serif" font-size="18" font-weight="400" letter-spacing="2">/ ID</text>
</svg>

After

Width:  |  Height:  |  Size: 948 B

-30
View File
@@ -1,30 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { i18nBuilder } from "keycloakify/account";
import type { ThemeName } from "../kc.gen";
/** @see: https://docs.keycloakify.dev/features/i18n */
const { useI18n, ofTypeI18n } = i18nBuilder
.withThemeName<ThemeName>()
.withCustomTranslations({
en: {
accountManagementTitle: "Account Management",
myResources: "My Resources",
nexusIdentityControl: "IDENTITY CONTROL",
nexusAccountUtilities: "Account utilities",
nexusAccountMatrix: "ACCOUNT MATRIX",
nexusSecureSession: "SECURE SESSION"
},
"zh-CN": {
accountManagementTitle: "账户管理",
myResources: "我的资源",
nexusIdentityControl: "身份控制",
nexusAccountUtilities: "账户操作",
nexusAccountMatrix: "账户矩阵",
nexusSecureSession: "安全会话"
}
})
.build();
type I18n = typeof ofTypeI18n;
export { useI18n, type I18n };
-40
View File
@@ -1,40 +0,0 @@
import type { Meta, StoryObj } from "@storybook/react";
import { createKcPageStory } from "../KcPageStory";
const { KcPageStory } = createKcPageStory({ pageId: "account.ftl" });
const meta = {
title: "account/account.ftl",
component: KcPageStory
} satisfies Meta<typeof KcPageStory>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => <KcPageStory />
};
export const SimplifiedChinese: Story = {
render: () => (
<KcPageStory
kcContext={{
locale: { currentLanguageTag: "zh-CN" }
}}
/>
)
};
export const WithSuccessMessage: Story = {
render: () => (
<KcPageStory
kcContext={{
message: {
type: "success",
summary: "Your account information has been updated."
}
}}
/>
)
};
-17
View File
@@ -1,17 +0,0 @@
import type { Meta, StoryObj } from "@storybook/react";
import { createKcPageStory } from "../KcPageStory";
const { KcPageStory } = createKcPageStory({ pageId: "password.ftl" });
const meta = {
title: "account/password.ftl",
component: KcPageStory
} satisfies Meta<typeof KcPageStory>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => <KcPageStory />
};
+196 -584
View File
@@ -1,633 +1,245 @@
:root { :root,
--nexus-account-void: #050713; .pf-v5-theme-dark {
--nexus-account-chrome: #091020; --nexus-account-void: #070b16;
--nexus-account-panel: #0f172a; --nexus-account-chrome: #0b1120;
--nexus-account-panel-raised: #141e34; --nexus-account-panel: #ffffff;
--nexus-account-ice: #63e6ff; --nexus-account-canvas: #f3f6fb;
--nexus-account-violet: #8b7cff; --nexus-account-ice: #2dc7e5;
--nexus-account-text: #f4f7ff; --nexus-account-violet: #7569f2;
--nexus-account-muted: #95a2be; --nexus-account-text: #172033;
--nexus-account-line: rgba(151, 172, 219, 0.18); --nexus-account-muted: #63708a;
--nexus-account-success: #54e6a4; --nexus-account-line: #dce3ef;
--nexus-account-danger: #ff668f; --pf-v5-global--primary-color--100: #007f9b;
--nexus-account-display: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif; --pf-v5-global--primary-color--200: #00677e;
--nexus-account-body-font: "Segoe UI Variable", "Segoe UI", sans-serif; --pf-v5-global--link--Color: #00758d;
--nexus-account-mono: "Cascadia Code", Consolas, monospace; --pf-v5-global--link--Color--hover: #004f62;
--pf-v5-global--BackgroundColor--100: var(--nexus-account-panel);
--pf-v5-global--BackgroundColor--150: #f8fafc;
--pf-v5-global--BackgroundColor--200: var(--nexus-account-canvas);
--pf-v5-global--Color--100: var(--nexus-account-text);
--pf-v5-global--Color--200: var(--nexus-account-muted);
--pf-v5-global--BorderColor--100: var(--nexus-account-line);
--pf-v5-global--BorderColor--200: #c9d3e2;
color-scheme: light;
} }
.nexus-account-html, html,
.nexus-account-body, body,
#root { #root {
min-height: 100%; min-height: 100%;
} }
.nexus-account-body { body {
min-height: 100vh;
margin: 0;
color: var(--nexus-account-text); color: var(--nexus-account-text);
background: var(--nexus-account-void); background: var(--nexus-account-canvas);
font-family: var(--nexus-account-body-font); font-family: "Segoe UI Variable", "Segoe UI", sans-serif;
font-size: 15px;
line-height: 1.5;
} }
.nexus-account-body *, .pf-v5-c-page,
.nexus-account-body *::before, .pf-v5-c-page__main,
.nexus-account-body *::after { .pf-v5-c-page__main-section,
box-sizing: border-box; .pf-v5-c-page__main-section.pf-m-light {
background-color: var(--nexus-account-canvas);
} }
.nexus-account-body button, .pf-v5-c-page__main {
.nexus-account-body input,
.nexus-account-body select,
.nexus-account-body textarea {
font: inherit;
}
.nexus-account-body a {
color: #a9eefe;
text-decoration: none;
}
.nexus-account-body a:hover {
color: #ffffff;
}
.nexus-account-shell {
min-height: 100vh;
background: radial-gradient(
circle at 10% 60%,
rgba(99, 230, 255, 0.07),
transparent 29rem
),
radial-gradient(circle at 88% 15%, rgba(139, 124, 255, 0.07), transparent 27rem),
linear-gradient(rgba(127, 155, 207, 0.028) 1px, transparent 1px),
linear-gradient(90deg, rgba(127, 155, 207, 0.028) 1px, transparent 1px),
var(--nexus-account-void);
background-size:
auto,
auto,
52px 52px,
52px 52px,
auto;
}
.nexus-account-header {
position: sticky;
z-index: 20;
top: 0;
display: grid;
min-height: 76px;
align-items: center;
grid-template-columns: 260px 1fr auto;
gap: 28px;
padding: 0 clamp(22px, 4vw, 58px);
border-bottom: 1px solid var(--nexus-account-line);
background: rgba(6, 10, 23, 0.88);
backdrop-filter: blur(18px);
}
.nexus-account-brand {
display: flex;
align-items: center;
gap: 12px;
color: var(--nexus-account-text) !important;
font-family: var(--nexus-account-display);
font-size: 0.92rem;
font-weight: 650;
letter-spacing: 0.18em;
}
.nexus-account-brand b {
color: var(--nexus-account-muted);
font-weight: 400;
}
.nexus-account-brand__mark {
position: relative; position: relative;
display: grid; isolation: isolate;
width: 28px; background-image: linear-gradient(rgba(32, 62, 104, 0.028) 1px, transparent 1px),
height: 28px; linear-gradient(90deg, rgba(32, 62, 104, 0.028) 1px, transparent 1px);
place-items: center; background-size: 44px 44px;
color: var(--nexus-account-ice);
font-size: 0.75rem;
} }
.nexus-account-brand__mark::before { .pf-v5-c-page__main::before,
position: absolute; .pf-v5-c-page__main::after {
inset: 3px; position: fixed;
border: 1px solid rgba(99, 230, 255, 0.52); z-index: -1;
box-shadow: right: -12rem;
inset 0 0 14px rgba(99, 230, 255, 0.1), bottom: -15rem;
0 0 16px rgba(99, 230, 255, 0.08); width: 34rem;
height: 34rem;
border: 1px solid rgba(45, 199, 229, 0.11);
border-radius: 50%;
content: "";
pointer-events: none;
}
.pf-v5-c-page__main::after {
right: -4rem;
bottom: -7rem;
width: 18rem;
height: 18rem;
border-color: rgba(117, 105, 242, 0.12);
box-shadow: 0 0 80px rgba(45, 199, 229, 0.06);
}
.pf-v5-c-masthead {
min-height: 68px;
border-bottom: 1px solid rgba(99, 230, 255, 0.18);
background: linear-gradient(90deg, rgba(99, 230, 255, 0.04), transparent 38%),
var(--nexus-account-void);
box-shadow: 0 8px 28px rgba(5, 9, 21, 0.18);
}
img.keycloak__pageheader_brand {
width: 190px;
height: 42px;
object-fit: contain;
object-position: left center;
}
.pf-v5-c-masthead .pf-v5-c-button.pf-m-link {
color: #b9c5da;
}
.pf-v5-c-page__sidebar,
.pf-v5-c-page__sidebar-body {
border-right: 1px solid rgba(117, 105, 242, 0.16);
color: #d8e2f5;
background: radial-gradient(circle at 18% 88%, rgba(99, 230, 255, 0.08), transparent 14rem),
var(--nexus-account-chrome);
}
.pf-v5-c-page__sidebar-body::before {
display: block;
width: 3.5rem;
height: 1px;
margin: 1.5rem 1.25rem 0.8rem;
background: linear-gradient(90deg, var(--nexus-account-ice), transparent);
box-shadow: 0.8rem 0.35rem 0 -0.12rem var(--nexus-account-violet);
content: "";
}
.pf-v5-c-nav__link,
.pf-v5-c-nav__section-title,
.pf-v5-c-nav__link.pf-m-expandable {
color: #9daac3;
}
.pf-v5-c-nav__link {
border-radius: 2px;
font-size: 0.88rem;
}
.pf-v5-c-nav__link:hover,
.pf-v5-c-nav__link:focus {
color: #ffffff;
background: rgba(99, 230, 255, 0.06);
}
.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-account-ice);
}
.pf-v5-c-nav__link.pf-m-current::after {
border-color: transparent;
}
.pf-v5-c-page__main-section:first-child {
border-bottom: 1px solid rgba(220, 227, 239, 0.8);
background: rgba(255, 255, 255, 0.78);
backdrop-filter: blur(12px);
}
.pf-v5-c-page__main-section + .pf-v5-c-page__main-section {
background: transparent;
}
.pf-v5-c-card,
.pf-v5-c-data-list,
.pf-v5-c-modal-box,
.pf-v5-c-drawer__panel,
.pf-v5-c-menu {
border: 1px solid var(--nexus-account-line);
border-radius: 3px;
background: var(--nexus-account-panel);
box-shadow: 0 15px 42px rgba(27, 43, 72, 0.08);
}
.pf-v5-c-title,
.pf-v5-c-content h1,
.pf-v5-c-content h2,
.pf-v5-c-page__main-title {
color: var(--nexus-account-text);
font-family: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif;
letter-spacing: -0.018em;
}
.pf-v5-c-title.pf-m-2xl::before {
display: inline-block;
width: 0.45rem;
height: 0.45rem;
margin: 0 0.75rem 0.18rem 0;
background: linear-gradient(135deg, var(--nexus-account-ice), var(--nexus-account-violet));
content: ""; content: "";
transform: rotate(45deg); transform: rotate(45deg);
} }
.nexus-account-header__realm { .pf-v5-c-button {
display: flex;
align-items: baseline;
gap: 10px;
min-width: 0;
}
.nexus-account-header__realm span,
.nexus-account-sidebar__label,
.nexus-account-sidebar__status {
color: var(--nexus-account-muted);
font-family: var(--nexus-account-mono);
font-size: 0.63rem;
letter-spacing: 0.15em;
}
.nexus-account-header__realm strong {
overflow: hidden;
color: #dce6fb;
font-size: 0.82rem;
font-weight: 550;
text-overflow: ellipsis;
white-space: nowrap;
}
.nexus-account-utility {
display: flex;
align-items: center;
gap: 18px;
font-size: 0.78rem;
}
.nexus-account-language {
position: relative;
}
.nexus-account-language summary {
color: var(--nexus-account-muted);
cursor: pointer;
list-style: none;
}
.nexus-account-language ul {
position: absolute;
top: calc(100% + 12px);
right: 0;
min-width: 150px;
max-height: 320px;
overflow: auto;
margin: 0;
padding: 7px;
border: 1px solid var(--nexus-account-line);
background: var(--nexus-account-panel-raised);
box-shadow: 0 18px 45px rgba(0, 0, 0, 0.38);
list-style: none;
}
.nexus-account-language li a {
display: block;
padding: 7px 9px;
}
.nexus-account-signout {
padding: 8px 12px;
border: 1px solid var(--nexus-account-line);
border-radius: 2px; border-radius: 2px;
font-weight: 620;
} }
.nexus-account-layout { .pf-v5-c-button.pf-m-primary {
display: grid; color: #ffffff;
min-height: calc(100vh - 76px); background: linear-gradient(105deg, #007f9b, #087f91 58%, #655bd7);
grid-template-columns: 280px minmax(0, 1fr); box-shadow: 0 6px 18px rgba(0, 127, 155, 0.16);
} }
.nexus-account-sidebar { .pf-v5-c-button.pf-m-primary:hover {
position: relative; filter: brightness(1.08);
display: flex;
min-height: calc(100vh - 76px);
overflow: hidden;
flex-direction: column;
padding: 40px 28px 28px clamp(24px, 4vw, 58px);
border-right: 1px solid var(--nexus-account-line);
background: rgba(8, 13, 28, 0.78);
} }
.nexus-account-orbit { .pf-v5-c-form-control,
position: absolute; .pf-v5-c-select__toggle,
right: -130px; .pf-v5-c-menu-toggle {
bottom: -100px;
width: 330px;
height: 330px;
border: 1px solid rgba(99, 230, 255, 0.1);
border-radius: 50%;
pointer-events: none;
}
.nexus-account-orbit::before,
.nexus-account-orbit::after {
position: absolute;
border: 1px dashed rgba(139, 124, 255, 0.13);
border-radius: 50%;
content: "";
}
.nexus-account-orbit::before {
inset: 18%;
}
.nexus-account-orbit::after {
inset: 36%;
}
.nexus-account-orbit span {
position: absolute;
top: 37%;
left: -4px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--nexus-account-ice);
box-shadow: 0 0 15px var(--nexus-account-ice);
}
.nexus-account-sidebar__label {
margin: 0 0 22px;
color: var(--nexus-account-ice);
}
.nexus-account-sidebar nav,
.nexus-account-sidebar nav ul {
position: relative;
z-index: 1;
margin: 0;
padding: 0;
list-style: none;
}
.nexus-account-sidebar li {
margin-bottom: 5px;
}
.nexus-account-sidebar li a {
display: flex;
align-items: center;
gap: 12px;
padding: 11px 12px;
border: 1px solid transparent;
border-radius: 2px; border-radius: 2px;
color: var(--nexus-account-muted); border-color: #c9d3e2;
font-size: 0.84rem; background: #ffffff;
transition: 150ms ease; box-shadow: none;
} }
.nexus-account-sidebar li a:hover { .pf-v5-c-form-control:focus-within,
border-color: var(--nexus-account-line); .pf-v5-c-menu-toggle:focus {
color: var(--nexus-account-text); border-color: #008eaa;
background: rgba(99, 230, 255, 0.04); box-shadow: 0 0 0 3px rgba(45, 199, 229, 0.12);
} }
.nexus-account-sidebar li.active a { .pf-v5-c-tabs__link::after {
border-color: rgba(99, 230, 255, 0.24);
color: var(--nexus-account-text);
background: linear-gradient(
90deg,
rgba(99, 230, 255, 0.11),
rgba(139, 124, 255, 0.04)
);
}
.nexus-account-nav__signal {
width: 5px;
height: 5px;
border-radius: 50%;
background: #52617e;
}
.nexus-account-sidebar li.active .nexus-account-nav__signal {
background: var(--nexus-account-ice);
box-shadow: 0 0 9px var(--nexus-account-ice);
}
.nexus-account-sidebar__status {
position: relative;
z-index: 1;
margin-top: auto;
padding-top: 18px;
border-top: 1px solid var(--nexus-account-line);
}
.nexus-account-sidebar__status span {
display: inline-block;
width: 5px;
height: 5px;
margin-right: 8px;
border-radius: 50%;
background: var(--nexus-account-success);
box-shadow: 0 0 8px var(--nexus-account-success);
}
.nexus-account-content {
min-width: 0;
padding: clamp(28px, 5vw, 70px);
}
.nexus-account-panel {
width: min(100%, 1080px);
min-height: 420px;
padding: clamp(28px, 4vw, 52px);
border: 1px solid var(--nexus-account-line);
border-radius: 3px;
background: linear-gradient(145deg, rgba(18, 27, 49, 0.94), rgba(10, 16, 32, 0.92));
box-shadow:
0 28px 80px rgba(0, 0, 0, 0.28),
inset 0 1px rgba(255, 255, 255, 0.03);
}
.nexus-account-panel > .row:first-child {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20px;
margin-bottom: 34px;
}
.nexus-account-panel h2 {
margin: 0;
color: var(--nexus-account-text);
font-family: var(--nexus-account-display);
font-size: clamp(2rem, 3vw, 2.7rem);
font-weight: 520;
letter-spacing: -0.035em;
line-height: 1.05;
}
.nexus-account-panel h2::after {
display: block;
width: 46px;
height: 2px;
margin-top: 17px;
background: linear-gradient(
90deg,
var(--nexus-account-ice),
var(--nexus-account-violet)
);
content: "";
}
.nexus-account-panel .subtitle {
color: var(--nexus-account-muted);
font-family: var(--nexus-account-mono);
font-size: 0.66rem;
letter-spacing: 0.08em;
}
.nexus-account-panel .required {
color: var(--nexus-account-danger);
}
.nexus-account-form,
.nexus-account-panel .form-horizontal {
width: 100%;
}
.nexus-account-field,
.nexus-account-panel .form-group {
display: grid;
align-items: center;
grid-template-columns: minmax(120px, 180px) minmax(0, 1fr);
gap: 18px;
margin-bottom: 20px;
}
.nexus-account-label,
.nexus-account-panel .control-label {
color: #bdc8df;
font-size: 0.8rem;
font-weight: 600;
}
.nexus-account-input,
.nexus-account-panel .form-control {
width: 100%;
min-height: 46px;
padding: 10px 14px;
border: 1px solid rgba(151, 172, 219, 0.24);
border-radius: 2px;
color: var(--nexus-account-text);
outline: 0;
background: rgba(4, 8, 21, 0.68);
transition: 150ms ease;
}
.nexus-account-input:focus,
.nexus-account-panel .form-control:focus {
border-color: var(--nexus-account-ice); border-color: var(--nexus-account-ice);
box-shadow: 0 0 0 3px rgba(99, 230, 255, 0.08);
} }
.nexus-account-input:disabled, .pf-v5-c-label {
.nexus-account-panel .form-control:disabled {
color: #6f7b96;
background: rgba(5, 8, 18, 0.5);
}
.nexus-account-button,
.nexus-account-panel .btn {
display: inline-flex;
min-height: 42px;
align-items: center;
justify-content: center;
margin: 0 8px 8px 0;
padding: 9px 18px;
border: 1px solid var(--nexus-account-line);
border-radius: 2px; border-radius: 2px;
color: var(--nexus-account-text);
background: rgba(18, 27, 49, 0.9);
cursor: pointer;
font-size: 0.82rem;
font-weight: 650;
} }
.nexus-account-button--primary { :focus-visible {
border-color: transparent; outline: 2px solid var(--nexus-account-ice) !important;
color: #06111c; outline-offset: 2px;
background: linear-gradient(105deg, var(--nexus-account-ice), #8cf0ec 52%, #a8a1ff);
} }
.nexus-account-panel .submit { @media (max-width: 768px) {
grid-column: 2; .pf-v5-c-masthead {
margin-top: 10px; min-height: 58px;
}
.nexus-account-panel table {
width: 100%;
border-collapse: collapse;
color: #dce4f5;
font-size: 0.82rem;
}
.nexus-account-panel th,
.nexus-account-panel td {
padding: 13px 14px;
border: 1px solid var(--nexus-account-line);
text-align: left;
vertical-align: top;
}
.nexus-account-panel th {
color: var(--nexus-account-muted);
background: rgba(5, 9, 21, 0.7);
font-family: var(--nexus-account-mono);
font-size: 0.65rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.nexus-account-panel tr:nth-child(even) td {
background: rgba(255, 255, 255, 0.018);
}
.nexus-account-alert {
display: flex;
width: min(100%, 1080px);
align-items: flex-start;
gap: 11px;
margin-bottom: 18px;
padding: 14px 16px;
border: 1px solid rgba(99, 230, 255, 0.25);
color: #dce6f8;
background: rgba(99, 230, 255, 0.06);
font-size: 0.82rem;
}
.nexus-account-alert--error {
border-color: rgba(255, 102, 143, 0.32);
background: rgba(255, 102, 143, 0.07);
}
.nexus-account-alert__signal {
width: 7px;
height: 7px;
flex: 0 0 auto;
margin-top: 6px;
border-radius: 50%;
background: var(--nexus-account-ice);
box-shadow: 0 0 8px currentColor;
}
.nexus-account-alert--error .nexus-account-alert__signal {
color: var(--nexus-account-danger);
background: currentColor;
}
.nexus-account-field-error,
.nexus-account-panel .has-error {
color: #ff93ae;
}
.nexus-account-body :focus-visible {
outline: 2px solid var(--nexus-account-ice);
outline-offset: 3px;
}
@media (max-width: 900px) {
.nexus-account-header {
grid-template-columns: 1fr auto;
} }
.nexus-account-header__realm { img.keycloak__pageheader_brand {
display: none; width: 158px;
height: 36px;
} }
.nexus-account-layout { .pf-v5-c-page__sidebar-body::before {
grid-template-columns: 1fr; margin-top: 1rem;
grid-template-rows: auto 1fr;
}
.nexus-account-sidebar {
min-height: auto;
padding: 18px 20px;
border-right: 0;
border-bottom: 1px solid var(--nexus-account-line);
}
.nexus-account-sidebar__label,
.nexus-account-sidebar__status,
.nexus-account-orbit {
display: none;
}
.nexus-account-sidebar ul {
display: flex;
overflow-x: auto;
gap: 6px;
padding-bottom: 4px !important;
scrollbar-width: none;
}
.nexus-account-sidebar ul::-webkit-scrollbar {
display: none;
}
.nexus-account-sidebar li {
flex: 0 0 auto;
margin: 0;
}
.nexus-account-content {
padding: 24px 20px 40px;
}
}
@media (max-width: 600px) {
.nexus-account-header {
min-height: 66px;
padding: 0 16px;
}
.nexus-account-brand {
font-size: 0.78rem;
}
.nexus-account-utility > a:not(.nexus-account-signout),
.nexus-account-language {
display: none;
}
.nexus-account-layout {
min-height: calc(100vh - 66px);
}
.nexus-account-content {
padding: 18px 12px 32px;
}
.nexus-account-panel {
padding: 26px 20px;
}
.nexus-account-panel > .row:first-child {
align-items: flex-start;
flex-direction: column;
gap: 14px;
}
.nexus-account-field,
.nexus-account-panel .form-group {
grid-template-columns: 1fr;
gap: 8px;
}
.nexus-account-panel .submit {
grid-column: 1;
}
.nexus-account-panel {
overflow-x: auto;
} }
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.nexus-account-body *, *,
.nexus-account-body *::before, *::before,
.nexus-account-body *::after { *::after {
animation-duration: 0.01ms !important; animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important; transition-duration: 0.01ms !important;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ export default defineConfig({
plugins: [ plugins: [
react(), react(),
keycloakify({ keycloakify({
accountThemeImplementation: "Multi-Page" accountThemeImplementation: "Single-Page"
}) })
] ]
}); });