Account:完整深色账户控制台、响应式侧栏、表单、表格和状态提示。

Admin:深色导航外壳、浅色操作区、统一按钮/表格/表单以及新 Logo。
Email:所有 HTML 邮件统一品牌卡片、渐变操作按钮和移动端布局。
This commit is contained in:
2026-08-03 09:48:59 +08:00 Unverified
parent 9253ab4e32
commit bb49401545
9 changed files with 1141 additions and 6 deletions
+3 -3
View File
@@ -1,6 +1,9 @@
# 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 ===
# admin/index.css
# admin/assets/logo.svg
# email/html/template.ftl
# === Owned files end ===== # === Owned files end =====
# === @keycloakify/email-native v260007.0.0 === # === @keycloakify/email-native v260007.0.0 ===
@@ -21,7 +24,6 @@
/email/html/identity-provider-link.ftl /email/html/identity-provider-link.ftl
/email/html/org-invite.ftl /email/html/org-invite.ftl
/email/html/password-reset.ftl /email/html/password-reset.ftl
/email/html/template.ftl
/email/messages/messages_ar.properties /email/messages/messages_ar.properties
/email/messages/messages_ca.properties /email/messages/messages_ca.properties
/email/messages/messages_cs.properties /email/messages/messages_cs.properties
@@ -79,7 +81,6 @@
/admin/ForbiddenSection.tsx /admin/ForbiddenSection.tsx
/admin/help-urls.ts /admin/help-urls.ts
/admin/i18next.d.ts /admin/i18next.d.ts
/admin/index.css
/admin/KcAdminUi.tsx /admin/KcAdminUi.tsx
/admin/KcContext.ts /admin/KcContext.ts
/admin/KcPage.tsx /admin/KcPage.tsx
@@ -94,7 +95,6 @@
/admin/assets/favicon.svg /admin/assets/favicon.svg
/admin/assets/icon.svg /admin/assets/icon.svg
/admin/assets/img_avatar.svg /admin/assets/img_avatar.svg
/admin/assets/logo.svg
/admin/authentication/AuthenticationSection.tsx /admin/authentication/AuthenticationSection.tsx
/admin/authentication/BindFlowDialog.tsx /admin/authentication/BindFlowDialog.tsx
/admin/authentication/build-in-label.module.css /admin/authentication/build-in-label.module.css
+25 -3
View File
@@ -3,7 +3,8 @@ import type { ClassKey } from "keycloakify/account";
import type { KcContext } from "./KcContext"; import type { KcContext } from "./KcContext";
import { useI18n } from "./i18n"; import { useI18n } from "./i18n";
import DefaultPage from "keycloakify/account/DefaultPage"; import DefaultPage from "keycloakify/account/DefaultPage";
import Template from "keycloakify/account/Template"; import Template from "./Template";
import "./theme.css";
export default function KcPage(props: { kcContext: KcContext }) { export default function KcPage(props: { kcContext: KcContext }) {
const { kcContext } = props; const { kcContext } = props;
@@ -15,11 +16,32 @@ export default function KcPage(props: { kcContext: KcContext }) {
{(() => { {(() => {
switch (kcContext.pageId) { switch (kcContext.pageId) {
default: default:
return <DefaultPage kcContext={kcContext} i18n={i18n} classes={classes} Template={Template} doUseDefaultCss={true} />; return (
<DefaultPage
kcContext={kcContext}
i18n={i18n}
classes={classes}
Template={Template}
doUseDefaultCss={false}
/>
);
} }
})()} })()}
</Suspense> </Suspense>
); );
} }
const classes = {} satisfies { [key in ClassKey]?: string }; const classes = {
kcHtmlClass: "nexus-account-html",
kcBodyClass: "nexus-account-body",
kcButtonClass: "nexus-account-button",
kcButtonPrimaryClass: "nexus-account-button--primary",
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 };
+124
View File
@@ -0,0 +1,124 @@
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>IDENTITY CONTROL</span>
<strong>{msg("accountManagementTitle")}</strong>
</div>
<nav className="nexus-account-utility" aria-label="Account utilities">
{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">ACCOUNT MATRIX</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 /> SECURE SESSION
</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>
);
}
+30
View File
@@ -0,0 +1,30 @@
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 WithSuccessMessage: Story = {
render: () => (
<KcPageStory
kcContext={{
message: {
type: "success",
summary: "Your account information has been updated."
}
}}
/>
)
};
+17
View File
@@ -0,0 +1,17 @@
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 />
};
+634
View File
@@ -0,0 +1,634 @@
:root {
--nexus-account-void: #050713;
--nexus-account-chrome: #091020;
--nexus-account-panel: #0f172a;
--nexus-account-panel-raised: #141e34;
--nexus-account-ice: #63e6ff;
--nexus-account-violet: #8b7cff;
--nexus-account-text: #f4f7ff;
--nexus-account-muted: #95a2be;
--nexus-account-line: rgba(151, 172, 219, 0.18);
--nexus-account-success: #54e6a4;
--nexus-account-danger: #ff668f;
--nexus-account-display: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif;
--nexus-account-body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
--nexus-account-mono: "Cascadia Code", Consolas, monospace;
}
.nexus-account-html,
.nexus-account-body,
#root {
min-height: 100%;
}
.nexus-account-body {
min-height: 100vh;
margin: 0;
color: var(--nexus-account-text);
background: var(--nexus-account-void);
font-family: var(--nexus-account-body-font);
font-size: 15px;
line-height: 1.5;
}
.nexus-account-body *,
.nexus-account-body *::before,
.nexus-account-body *::after {
box-sizing: border-box;
}
.nexus-account-body button,
.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;
display: grid;
width: 28px;
height: 28px;
place-items: center;
color: var(--nexus-account-ice);
font-size: 0.75rem;
}
.nexus-account-brand__mark::before {
position: absolute;
inset: 3px;
border: 1px solid rgba(99, 230, 255, 0.52);
box-shadow:
inset 0 0 14px rgba(99, 230, 255, 0.1),
0 0 16px rgba(99, 230, 255, 0.08);
content: "";
transform: rotate(45deg);
}
.nexus-account-header__realm {
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;
}
.nexus-account-layout {
display: grid;
min-height: calc(100vh - 76px);
grid-template-columns: 280px minmax(0, 1fr);
}
.nexus-account-sidebar {
position: relative;
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 {
position: absolute;
right: -130px;
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;
color: var(--nexus-account-muted);
font-size: 0.84rem;
transition: 150ms ease;
}
.nexus-account-sidebar li a:hover {
border-color: var(--nexus-account-line);
color: var(--nexus-account-text);
background: rgba(99, 230, 255, 0.04);
}
.nexus-account-sidebar li.active a {
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);
box-shadow: 0 0 0 3px rgba(99, 230, 255, 0.08);
}
.nexus-account-input:disabled,
.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;
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 {
border-color: transparent;
color: #06111c;
background: linear-gradient(105deg, var(--nexus-account-ice), #8cf0ec 52%, #a8a1ff);
}
.nexus-account-panel .submit {
grid-column: 2;
margin-top: 10px;
}
.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 {
display: none;
}
.nexus-account-layout {
grid-template-columns: 1fr;
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) {
.nexus-account-body *,
.nexus-account-body *::before,
.nexus-account-body *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
+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

+232
View File
@@ -0,0 +1,232 @@
:root,
.pf-v5-theme-dark {
--nexus-admin-void: #070b16;
--nexus-admin-chrome: #0b1120;
--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;
--pf-v5-global--BackgroundColor--100: var(--nexus-admin-panel);
--pf-v5-global--BackgroundColor--150: #f7f9fc;
--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-c-page__main-section--m-light--BackgroundColor: var(--nexus-admin-canvas);
color-scheme: light;
}
html,
body,
#root {
min-height: 100%;
}
body {
color: var(--nexus-admin-text);
background: var(--nexus-admin-canvas);
font-family: "Segoe UI Variable", "Segoe UI", sans-serif;
}
.pf-v5-c-page,
.pf-v5-c-page__main,
.pf-v5-c-page__main-section {
background-color: var(--nexus-admin-canvas);
}
.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;
}
.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%),
var(--nexus-admin-void);
box-shadow: 0 8px 28px rgba(5, 9, 21, 0.18);
}
img.keycloak__pageheader_brand {
width: 190px;
height: 38px;
object-fit: contain;
object-position: left center;
}
.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 20% 88%,
rgba(99, 230, 255, 0.07),
transparent 14rem
),
var(--nexus-admin-chrome);
}
.pf-v5-c-nav__link {
border-radius: 2px;
color: #9daac3;
font-size: 0.86rem;
}
.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-admin-ice);
}
.pf-v5-c-nav__link.pf-m-current::after {
border-color: transparent;
}
.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-title,
.pf-v5-c-content h1,
.pf-v5-c-content h2,
.pf-v5-c-page__main-title {
color: var(--nexus-admin-text);
font-family: "Bahnschrift SemiCondensed", "Arial Narrow", sans-serif;
letter-spacing: -0.018em;
}
.pf-v5-c-page__main-breadcrumb {
border-bottom: 1px solid rgba(220, 227, 239, 0.74);
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(12px);
}
.pf-v5-c-button {
border-radius: 2px;
font-weight: 620;
}
.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-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: #c9d3e2;
background: #ffffff;
box-shadow: none;
}
.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-table {
border: 1px solid var(--nexus-admin-line);
background: #ffffff;
}
.pf-v5-c-table thead {
color: #526079;
background: #edf2f8;
font-family: "Cascadia Code", Consolas, monospace;
font-size: 0.72rem;
letter-spacing: 0.025em;
}
.pf-v5-c-table tr:hover > * {
background-color: #f5fbfd;
}
.pf-v5-c-tabs__link::after {
border-color: var(--nexus-admin-ice);
}
.pf-v5-c-label {
border-radius: 2px;
}
.keycloak__form {
max-width: 1100px;
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;
z-index: 15;
bottom: 0;
width: 100%;
margin-left: calc(-1 * var(--pf-v5-global--spacer--lg));
padding: var(--pf-v5-global--spacer--md);
border-top: 1px solid var(--nexus-admin-line);
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 -10px 30px rgba(27, 43, 72, 0.06);
backdrop-filter: blur(14px);
}
.keycloak__section_intro__help {
width: fit-content;
padding: var(--pf-v5-global--spacer--md) 0 var(--pf-v5-global--spacer--lg);
color: var(--pf-v5-global--primary-color--100);
}
div.w-tc-editor {
font-family: "Cascadia Code", "SFMono-Regular", Consolas, monospace;
}
:focus-visible {
outline: 2px solid var(--nexus-admin-ice) !important;
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
+60
View File
@@ -0,0 +1,60 @@
<#macro emailLayout>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { margin: 0; padding: 0; background: #050713; color: #f4f7ff; font-family: "Segoe UI", Arial, sans-serif; }
table { border-collapse: collapse; }
a { color: #047f9a; font-weight: 600; }
.nexus-email-shell { width: 100%; background: #050713; }
.nexus-email-wrap { width: 100%; max-width: 620px; margin: 0 auto; padding: 44px 18px; }
.nexus-email-brand { padding: 0 0 18px; color: #f4f7ff; font-family: Arial, sans-serif; font-size: 14px; font-weight: 700; letter-spacing: 3px; }
.nexus-email-signal { display: inline-block; width: 8px; height: 8px; margin: 0 12px 1px 2px; background: #63e6ff; transform: rotate(45deg); }
.nexus-email-brand-muted { color: #8e9ab7; font-weight: 400; }
.nexus-email-card { border: 1px solid #25314d; background: #0f172a; }
.nexus-email-accent { height: 3px; background: #63e6ff; background: linear-gradient(90deg, #63e6ff, #8cf0ec 50%, #8b7cff); }
.nexus-email-content { padding: 38px 42px 42px; color: #dce4f5; font-size: 15px; line-height: 1.7; }
.nexus-email-content h1, .nexus-email-content h2 { margin-top: 0; color: #ffffff; font-family: Arial, sans-serif; line-height: 1.15; }
.nexus-email-content p { margin: 0 0 18px; }
.nexus-email-content a { display: inline-block; margin: 8px 0; padding: 12px 20px; border-radius: 2px; color: #06111c !important; background: #63e6ff; background: linear-gradient(105deg, #63e6ff, #8cf0ec 55%, #a8a1ff); text-decoration: none; }
.nexus-email-meta { padding: 18px 2px 0; color: #7f8ca8; font-family: Consolas, monospace; font-size: 10px; letter-spacing: 1.4px; line-height: 1.7; }
.nexus-email-online { color: #54e6a4; }
@media only screen and (max-width: 540px) {
.nexus-email-wrap { padding: 22px 10px !important; }
.nexus-email-content { padding: 28px 22px 32px !important; }
}
</style>
</head>
<body>
<table role="presentation" class="nexus-email-shell" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table role="presentation" class="nexus-email-wrap" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="nexus-email-brand">
<span class="nexus-email-signal"></span>NEXUS <span class="nexus-email-brand-muted">/ ID</span>
</td>
</tr>
<tr>
<td class="nexus-email-card">
<div class="nexus-email-accent"></div>
<div class="nexus-email-content">
<#nested>
</div>
</td>
</tr>
<tr>
<td class="nexus-email-meta">
IDENTITY ACCESS LAYER&nbsp;&nbsp;/&nbsp;&nbsp;<span class="nexus-email-online">SECURE MESSAGE</span><br>
This message was generated automatically. Please do not reply.
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
</#macro>