diff --git a/src/.gitignore b/src/.gitignore index d6aaf46..82bbf00 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,6 +1,9 @@ # This file is managed by Keycloakify, do not edit it manually. # === Owned files start === +# admin/index.css +# admin/assets/logo.svg +# email/html/template.ftl # === Owned files end ===== # === @keycloakify/email-native v260007.0.0 === @@ -21,7 +24,6 @@ /email/html/identity-provider-link.ftl /email/html/org-invite.ftl /email/html/password-reset.ftl -/email/html/template.ftl /email/messages/messages_ar.properties /email/messages/messages_ca.properties /email/messages/messages_cs.properties @@ -79,7 +81,6 @@ /admin/ForbiddenSection.tsx /admin/help-urls.ts /admin/i18next.d.ts -/admin/index.css /admin/KcAdminUi.tsx /admin/KcContext.ts /admin/KcPage.tsx @@ -94,7 +95,6 @@ /admin/assets/favicon.svg /admin/assets/icon.svg /admin/assets/img_avatar.svg -/admin/assets/logo.svg /admin/authentication/AuthenticationSection.tsx /admin/authentication/BindFlowDialog.tsx /admin/authentication/build-in-label.module.css diff --git a/src/account/KcPage.tsx b/src/account/KcPage.tsx index 6f86dc7..baf935a 100644 --- a/src/account/KcPage.tsx +++ b/src/account/KcPage.tsx @@ -3,7 +3,8 @@ import type { ClassKey } from "keycloakify/account"; import type { KcContext } from "./KcContext"; import { useI18n } from "./i18n"; 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 }) { const { kcContext } = props; @@ -15,11 +16,32 @@ export default function KcPage(props: { kcContext: KcContext }) { {(() => { switch (kcContext.pageId) { default: - return ; + return ( + + ); } })()} ); } -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 }; diff --git a/src/account/Template.tsx b/src/account/Template.tsx new file mode 100644 index 0000000..2a42ea6 --- /dev/null +++ b/src/account/Template.tsx @@ -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) { + 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 ( +
+
+ + N + + NEXUS / ID + + + +
+ IDENTITY CONTROL + {msg("accountManagementTitle")} +
+ + +
+ +
+ + +
+ {message !== undefined && ( +
+ + +
+ )} +
{children}
+
+
+
+ ); +} diff --git a/src/account/pages/Account.stories.tsx b/src/account/pages/Account.stories.tsx new file mode 100644 index 0000000..4ca3b49 --- /dev/null +++ b/src/account/pages/Account.stories.tsx @@ -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; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => +}; + +export const WithSuccessMessage: Story = { + render: () => ( + + ) +}; diff --git a/src/account/pages/Password.stories.tsx b/src/account/pages/Password.stories.tsx new file mode 100644 index 0000000..ecd1b0e --- /dev/null +++ b/src/account/pages/Password.stories.tsx @@ -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; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => +}; diff --git a/src/account/theme.css b/src/account/theme.css new file mode 100644 index 0000000..05c17e7 --- /dev/null +++ b/src/account/theme.css @@ -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; + } +} diff --git a/src/admin/assets/logo.svg b/src/admin/assets/logo.svg new file mode 100644 index 0000000..5a6a72d --- /dev/null +++ b/src/admin/assets/logo.svg @@ -0,0 +1,16 @@ + + NEXUS ID + + + + + + + + + + + + NEXUS + / ID + diff --git a/src/admin/index.css b/src/admin/index.css new file mode 100644 index 0000000..9a22a22 --- /dev/null +++ b/src/admin/index.css @@ -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; + } +} diff --git a/src/email/html/template.ftl b/src/email/html/template.ftl new file mode 100644 index 0000000..a113235 --- /dev/null +++ b/src/email/html/template.ftl @@ -0,0 +1,60 @@ +<#macro emailLayout> + + + + + + + + + + + + + + + +