Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
992c11ebb6 | ||
|
|
0a9baf4953 | ||
|
|
1d96de33d7 | ||
|
|
d17e1865ba | ||
|
|
cd7211e367 | ||
|
|
76f56c1571 |
+10
-10
@@ -100,16 +100,15 @@ const classes = {
|
|||||||
kcBodyClass: "",
|
kcBodyClass: "",
|
||||||
|
|
||||||
kcLoginClass:
|
kcLoginClass:
|
||||||
"relative z-[1] min-h-screen flex flex-col items-center pt-[15vh] px-4 pb-8 sm:px-[18px] mac-slide-up",
|
"relative z-[1] min-h-screen flex flex-col items-center justify-center px-4 pb-8 sm:px-[18px] mac-slide-up",
|
||||||
|
|
||||||
kcHeaderWrapperClass: "max-w-[600px] mx-auto",
|
kcHeaderWrapperClass: "max-w-[600px] mx-auto",
|
||||||
kcHeaderClass:
|
kcHeaderClass: "mb-6 text-center glass-text-stable",
|
||||||
"mb-6 text-center text-[0.93rem] tracking-[0.01em] text-muted-foreground",
|
|
||||||
|
|
||||||
kcFormCardClass:
|
kcFormCardClass:
|
||||||
"relative w-full sm:w-[460px] rounded-xl border border-border bg-card px-4 py-5 sm:px-7 sm:py-[30px]",
|
"glass relative w-full sm:w-[460px] rounded-xl px-4 py-5 sm:px-7 sm:py-[30px]",
|
||||||
|
|
||||||
kcFormHeaderClass: "relative grid grid-cols-[1fr_auto] gap-3 items-baseline mb-1",
|
kcFormHeaderClass: "relative grid grid-cols-[1fr_auto] gap-3 items-center mb-1",
|
||||||
kcLocaleMainClass: "m-0 col-start-2 row-start-1 flex items-center",
|
kcLocaleMainClass: "m-0 col-start-2 row-start-1 flex items-center",
|
||||||
|
|
||||||
kcContentClass: "mt-[18px]",
|
kcContentClass: "mt-[18px]",
|
||||||
@@ -161,13 +160,14 @@ const classes = {
|
|||||||
kcSelectAuthListItemIconClass:
|
kcSelectAuthListItemIconClass:
|
||||||
"shrink-0 w-10 h-10 rounded-full grid place-items-center bg-muted text-foreground",
|
"shrink-0 w-10 h-10 rounded-full grid place-items-center bg-muted text-foreground",
|
||||||
kcSelectAuthListItemIconPropertyClass: "",
|
kcSelectAuthListItemIconPropertyClass: "",
|
||||||
kcSelectAuthListItemArrowClass: "",
|
kcSelectAuthListItemArrowClass: "shrink-0 ml-auto",
|
||||||
kcSelectAuthListItemArrowIconClass: "flex-1 min-w-0 flex flex-col gap-0.5",
|
kcSelectAuthListItemArrowIconClass: "",
|
||||||
kcSelectAuthListItemBodyClass: "",
|
kcSelectAuthListItemBodyClass:
|
||||||
|
"flex-1 min-w-0 flex flex-col items-center gap-0.5 text-center",
|
||||||
kcSelectAuthListItemHeadingClass:
|
kcSelectAuthListItemHeadingClass:
|
||||||
"text-[0.96rem] font-semibold text-foreground leading-tight",
|
"text-[0.96rem] font-semibold text-foreground leading-tight text-center",
|
||||||
kcSelectAuthListItemDescriptionClass:
|
kcSelectAuthListItemDescriptionClass:
|
||||||
"text-[0.82rem] font-medium text-muted-foreground leading-snug flex flex-wrap gap-1",
|
"text-[0.82rem] font-medium text-muted-foreground leading-snug text-center",
|
||||||
kcSelectAuthListItemFillClass: "hidden",
|
kcSelectAuthListItemFillClass: "hidden",
|
||||||
kcSelectAuthListItemTitle:
|
kcSelectAuthListItemTitle:
|
||||||
"m-0 mb-1 text-[0.9rem] font-semibold text-muted-foreground",
|
"m-0 mb-1 text-[0.9rem] font-semibold text-muted-foreground",
|
||||||
|
|||||||
+103
-100
@@ -5,7 +5,8 @@ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
|
|||||||
import { useSetClassName } from "keycloakify/tools/useSetClassName";
|
import { useSetClassName } from "keycloakify/tools/useSetClassName";
|
||||||
import { useInitialize } from "keycloakify/login/Template.useInitialize";
|
import { useInitialize } from "keycloakify/login/Template.useInitialize";
|
||||||
import type { TemplateProps } from "keycloakify/login/TemplateProps";
|
import type { TemplateProps } from "keycloakify/login/TemplateProps";
|
||||||
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
|
import { CardContent, CardFooter, CardHeader } from "@/components/ui/card";
|
||||||
|
import { GlassAmbientBackground, GlassCard } from "./glass";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import type { KcContext } from "./KcContext";
|
import type { KcContext } from "./KcContext";
|
||||||
import type { I18n } from "./i18n";
|
import type { I18n } from "./i18n";
|
||||||
@@ -86,106 +87,108 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={kcClsx("kcLoginClass")}>
|
<GlassAmbientBackground>
|
||||||
<div id="kc-header" className={kcClsx("kcHeaderClass")}>
|
<div className={kcClsx("kcLoginClass")}>
|
||||||
<div id="kc-header-wrapper" className={kcClsx("kcHeaderWrapperClass")}>
|
<div id="kc-header" className={kcClsx("kcHeaderClass")}>
|
||||||
{msg("loginTitleHtml", realm.displayNameHtml || realm.name)}
|
<div id="kc-header-wrapper" className={kcClsx("kcHeaderWrapperClass")}>
|
||||||
</div>
|
{msg("loginTitleHtml", realm.displayNameHtml || realm.name)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card className={clsx(kcClsx("kcFormCardClass"), "w-full sm:w-[460px]")}>
|
|
||||||
<CardHeader className={clsx(kcClsx("kcFormHeaderClass"), "space-y-0 p-0 pb-4")}>
|
|
||||||
{enabledLanguages.length > 1 && (
|
|
||||||
<div className={kcClsx("kcLocaleMainClass")} id="kc-locale">
|
|
||||||
<div id="kc-locale-wrapper" className={kcClsx("kcLocaleWrapperClass")}>
|
|
||||||
<div id="kc-locale-dropdown" className={clsx("menu-button-links", kcClsx("kcLocaleDropDownClass"))}>
|
|
||||||
<button
|
|
||||||
tabIndex={1}
|
|
||||||
id="kc-current-locale-link"
|
|
||||||
aria-label={msgStr("languages")}
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-controls="language-switch1"
|
|
||||||
>
|
|
||||||
{currentLanguage.label}
|
|
||||||
</button>
|
|
||||||
<ul
|
|
||||||
role="menu"
|
|
||||||
tabIndex={-1}
|
|
||||||
aria-labelledby="kc-current-locale-link"
|
|
||||||
aria-activedescendant=""
|
|
||||||
id="language-switch1"
|
|
||||||
className={kcClsx("kcLocaleListClass")}
|
|
||||||
>
|
|
||||||
{enabledLanguages.map(({ languageTag, label, href }, i: number) => (
|
|
||||||
<li key={languageTag} className={kcClsx("kcLocaleListItemClass")} role="none">
|
|
||||||
<a role="menuitem" id={`language-${i + 1}`} className={kcClsx("kcLocaleItemClass")} href={href}>
|
|
||||||
{label}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{header}
|
|
||||||
</CardHeader>
|
|
||||||
|
|
||||||
<CardContent id="kc-content" className="p-0">
|
|
||||||
<div id="kc-content-wrapper">
|
|
||||||
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
|
|
||||||
<Alert
|
|
||||||
className={clsx(
|
|
||||||
`alert-${message.type}`,
|
|
||||||
kcClsx("kcAlertClass"),
|
|
||||||
`pf-m-${message.type === "error" ? "danger" : message.type}`,
|
|
||||||
"mb-4"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<AlertDescription
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: kcSanitize(message.summary)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{children}
|
|
||||||
|
|
||||||
{auth !== undefined && auth.showTryAnotherWayLink && (
|
|
||||||
<form id="kc-select-try-another-way-form" action={url.loginAction} method="post">
|
|
||||||
<div className={kcClsx("kcFormGroupClass")}>
|
|
||||||
<input type="hidden" name="tryAnotherWay" value="on" />
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
id="try-another-way"
|
|
||||||
onClick={event => {
|
|
||||||
(document.forms.namedItem("kc-select-try-another-way-form") as HTMLFormElement).requestSubmit();
|
|
||||||
event.preventDefault();
|
|
||||||
return false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{msg("doTryAnotherWay")}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{socialProvidersNode}
|
|
||||||
|
|
||||||
{displayInfo && (
|
|
||||||
<div id="kc-info" className={kcClsx("kcSignUpClass")}>
|
|
||||||
<div id="kc-info-wrapper" className={kcClsx("kcInfoAreaWrapperClass")}>
|
|
||||||
{infoNode}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
|
|
||||||
{infoNode && !displayInfo && <CardFooter className="p-0 pt-4">{infoNode}</CardFooter>}
|
<GlassCard className={clsx(kcClsx("kcFormCardClass"), "w-full sm:w-[460px]")}>
|
||||||
</Card>
|
<CardHeader className={clsx(kcClsx("kcFormHeaderClass"), "space-y-0 p-0 pb-4")}>
|
||||||
</div>
|
{enabledLanguages.length > 1 && (
|
||||||
|
<div className={kcClsx("kcLocaleMainClass")} id="kc-locale">
|
||||||
|
<div id="kc-locale-wrapper" className={kcClsx("kcLocaleWrapperClass")}>
|
||||||
|
<div id="kc-locale-dropdown" className={clsx("menu-button-links", kcClsx("kcLocaleDropDownClass"))}>
|
||||||
|
<button
|
||||||
|
tabIndex={1}
|
||||||
|
id="kc-current-locale-link"
|
||||||
|
aria-label={msgStr("languages")}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="language-switch1"
|
||||||
|
>
|
||||||
|
{currentLanguage.label}
|
||||||
|
</button>
|
||||||
|
<ul
|
||||||
|
role="menu"
|
||||||
|
tabIndex={-1}
|
||||||
|
aria-labelledby="kc-current-locale-link"
|
||||||
|
aria-activedescendant=""
|
||||||
|
id="language-switch1"
|
||||||
|
className={kcClsx("kcLocaleListClass")}
|
||||||
|
>
|
||||||
|
{enabledLanguages.map(({ languageTag, label, href }, i: number) => (
|
||||||
|
<li key={languageTag} className={kcClsx("kcLocaleListItemClass")} role="none">
|
||||||
|
<a role="menuitem" id={`language-${i + 1}`} className={kcClsx("kcLocaleItemClass")} href={href}>
|
||||||
|
{label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{header}
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent id="kc-content" className="p-0">
|
||||||
|
<div id="kc-content-wrapper">
|
||||||
|
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
|
||||||
|
<Alert
|
||||||
|
className={clsx(
|
||||||
|
`alert-${message.type}`,
|
||||||
|
kcClsx("kcAlertClass"),
|
||||||
|
`pf-m-${message.type === "error" ? "danger" : message.type}`,
|
||||||
|
"mb-4"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<AlertDescription
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: kcSanitize(message.summary)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{children}
|
||||||
|
|
||||||
|
{auth !== undefined && auth.showTryAnotherWayLink && (
|
||||||
|
<form id="kc-select-try-another-way-form" action={url.loginAction} method="post">
|
||||||
|
<div className={kcClsx("kcFormGroupClass")}>
|
||||||
|
<input type="hidden" name="tryAnotherWay" value="on" />
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
id="try-another-way"
|
||||||
|
onClick={event => {
|
||||||
|
(document.forms.namedItem("kc-select-try-another-way-form") as HTMLFormElement).requestSubmit();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{msg("doTryAnotherWay")}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{socialProvidersNode}
|
||||||
|
|
||||||
|
{displayInfo && (
|
||||||
|
<div id="kc-info" className={kcClsx("kcSignUpClass")}>
|
||||||
|
<div id="kc-info-wrapper" className={kcClsx("kcInfoAreaWrapperClass")}>
|
||||||
|
{infoNode}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
{infoNode && !displayInfo && <CardFooter className="p-0 pt-4 border-t border-border">{infoNode}</CardFooter>}
|
||||||
|
</GlassCard>
|
||||||
|
</div>
|
||||||
|
</GlassAmbientBackground>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animated ambient background for the glassmorphism login theme.
|
||||||
|
* Uses only CSS transforms/opacity for GPU-composited motion.
|
||||||
|
* Respects `prefers-reduced-motion` (animation stops) and is aria-hidden.
|
||||||
|
*/
|
||||||
|
export function GlassAmbientBackground({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
className="fixed inset-0 z-0 overflow-hidden pointer-events-none"
|
||||||
|
>
|
||||||
|
{/* Large, heavily blurred color blobs */}
|
||||||
|
<div
|
||||||
|
className="glass-ambient-blob"
|
||||||
|
style={{
|
||||||
|
width: "70vw",
|
||||||
|
height: "70vw",
|
||||||
|
left: "-22vw",
|
||||||
|
top: "-18vh",
|
||||||
|
background: "var(--glass-ambient-1)",
|
||||||
|
animationDuration: "30s",
|
||||||
|
animationDelay: "-6s"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="glass-ambient-blob"
|
||||||
|
style={{
|
||||||
|
width: "60vw",
|
||||||
|
height: "60vw",
|
||||||
|
right: "-16vw",
|
||||||
|
top: "2vh",
|
||||||
|
background: "var(--glass-ambient-2)",
|
||||||
|
animationDuration: "34s",
|
||||||
|
animationDelay: "-14s"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="glass-ambient-blob"
|
||||||
|
style={{
|
||||||
|
width: "55vw",
|
||||||
|
height: "55vw",
|
||||||
|
left: "10vw",
|
||||||
|
bottom: "-20vh",
|
||||||
|
background: "var(--glass-ambient-3)",
|
||||||
|
animationDuration: "38s",
|
||||||
|
animationDelay: "-10s"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="glass-ambient-blob"
|
||||||
|
style={{
|
||||||
|
width: "48vw",
|
||||||
|
height: "48vw",
|
||||||
|
right: "6vw",
|
||||||
|
bottom: "-8vh",
|
||||||
|
background: "var(--glass-ambient-4)",
|
||||||
|
animationDuration: "32s",
|
||||||
|
animationDelay: "-22s"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Long flowing aurora bands */}
|
||||||
|
<div
|
||||||
|
className="glass-ambient-aurora"
|
||||||
|
style={{
|
||||||
|
width: "80vw",
|
||||||
|
height: "30vh",
|
||||||
|
left: "-10vw",
|
||||||
|
top: "35vh",
|
||||||
|
background: "var(--glass-ambient-5)",
|
||||||
|
animationDuration: "40s",
|
||||||
|
animationDelay: "-4s"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="glass-ambient-aurora"
|
||||||
|
style={{
|
||||||
|
width: "70vw",
|
||||||
|
height: "24vh",
|
||||||
|
right: "-12vw",
|
||||||
|
bottom: "25vh",
|
||||||
|
background: "var(--glass-ambient-6)",
|
||||||
|
animationDuration: "44s",
|
||||||
|
animationDelay: "-18s"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Soft vignette to focus attention on the card */}
|
||||||
|
<div className="glass-vignette" />
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Glass card wrapper.
|
||||||
|
* Applies the base `.glass` utility and keeps the same flex/gap contract
|
||||||
|
* as the shadcn Card so upstream changes around the Card component are easy
|
||||||
|
* to merge: only this isolated component needs to be updated.
|
||||||
|
*/
|
||||||
|
export function GlassCard({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"glass relative flex flex-col gap-6 p-6 sm:p-8 text-card-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
/**
|
||||||
|
* Glassmorphism / Liquid Glass theme tokens
|
||||||
|
*
|
||||||
|
* Isolated module: all glass-specific tokens and utilities live here.
|
||||||
|
* Changing upstream Keycloakify or shadcn files should not affect this file,
|
||||||
|
* and changes here are intentionally scoped to the login theme.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Blur radius — keep between 10–20px for GPU/UX balance */
|
||||||
|
--glass-blur: 14px;
|
||||||
|
--glass-blur-heavy: 20px;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Light theme glass tokens.
|
||||||
|
* Higher opacity than typical Dribbble glass so the card is readable
|
||||||
|
* and clearly separated from the animated background.
|
||||||
|
*/
|
||||||
|
--glass-bg: hsla(0 0% 100% / 0.42);
|
||||||
|
--glass-bg-solid: hsla(0 0% 100% / 0.82);
|
||||||
|
--glass-bg-scrim: hsla(0 0% 100% / 0.62);
|
||||||
|
--glass-border: hsla(0 0% 100% / 0.58);
|
||||||
|
--glass-border-highlight: hsla(0 0% 100% / 0.85);
|
||||||
|
--glass-shadow: 0 14px 44px hsla(220 40% 16% / 0.18);
|
||||||
|
--glass-shadow-elevated: 0 24px 64px hsla(220 40% 12% / 0.24);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Soft ambient background palette.
|
||||||
|
* Pastel, heavily blurred blobs for a calm liquid-glass feeling.
|
||||||
|
*/
|
||||||
|
--glass-ambient-1: hsla(250 90% 70% / 0.45);
|
||||||
|
--glass-ambient-2: hsla(320 85% 68% / 0.4);
|
||||||
|
--glass-ambient-3: hsla(190 90% 62% / 0.38);
|
||||||
|
--glass-ambient-4: hsla(40 95% 65% / 0.34);
|
||||||
|
--glass-ambient-5: hsla(140 80% 60% / 0.26);
|
||||||
|
--glass-ambient-6: hsla(10 90% 66% / 0.3);
|
||||||
|
|
||||||
|
/* Text stability on busy backgrounds */
|
||||||
|
--glass-text-shadow: 0 1px 2px hsla(0 0% 0% / 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
/* Dark glass needs to be noticeably darker/tinted or it disappears on a dark page */
|
||||||
|
--glass-bg: hsla(220 20% 14% / 0.62);
|
||||||
|
--glass-bg-solid: hsla(220 20% 10% / 0.9);
|
||||||
|
--glass-bg-scrim: hsla(220 20% 8% / 0.78);
|
||||||
|
--glass-border: hsla(0 0% 100% / 0.18);
|
||||||
|
--glass-border-highlight: hsla(0 0% 100% / 0.32);
|
||||||
|
--glass-shadow: 0 14px 44px hsla(0 0% 0% / 0.45);
|
||||||
|
--glass-shadow-elevated: 0 24px 64px hsla(0 0% 0% / 0.55);
|
||||||
|
|
||||||
|
/* Dark-mode blobs: vivid enough to refract, soft enough to not distract */
|
||||||
|
--glass-ambient-1: hsla(250 90% 58% / 0.38);
|
||||||
|
--glass-ambient-2: hsla(320 85% 56% / 0.34);
|
||||||
|
--glass-ambient-3: hsla(190 90% 54% / 0.32);
|
||||||
|
--glass-ambient-4: hsla(40 95% 58% / 0.28);
|
||||||
|
--glass-ambient-5: hsla(140 80% 50% / 0.22);
|
||||||
|
--glass-ambient-6: hsla(10 90% 58% / 0.26);
|
||||||
|
|
||||||
|
--glass-text-shadow: 0 1px 3px hsla(0 0% 0% / 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduced transparency: make glass almost opaque and drop the blur */
|
||||||
|
@media (prefers-reduced-transparency: reduce) {
|
||||||
|
:root {
|
||||||
|
--glass-bg: hsla(0 0% 100% / 0.95);
|
||||||
|
--glass-bg-solid: hsla(0 0% 100% / 0.99);
|
||||||
|
--glass-bg-scrim: hsla(0 0% 100% / 0.99);
|
||||||
|
--glass-border: hsla(0 0% 0% / 0.14);
|
||||||
|
--glass-border-highlight: hsla(0 0% 0% / 0.1);
|
||||||
|
--glass-blur: 2px;
|
||||||
|
--glass-blur-heavy: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--glass-bg: hsla(220 20% 8% / 0.97);
|
||||||
|
--glass-bg-solid: hsla(220 20% 6% / 0.99);
|
||||||
|
--glass-bg-scrim: hsla(220 20% 6% / 0.99);
|
||||||
|
--glass-border: hsla(0 0% 100% / 0.14);
|
||||||
|
--glass-border-highlight: hsla(0 0% 100% / 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base glass layer — use for cards, panels, overlays */
|
||||||
|
.glass {
|
||||||
|
/* Tinted fill plus a subtle top-left sheen to mimic physical glass */
|
||||||
|
background: linear-gradient(
|
||||||
|
145deg,
|
||||||
|
hsla(0 0% 100% / 0.22) 0%,
|
||||||
|
hsla(0 0% 100% / 0.06) 55%,
|
||||||
|
hsla(0 0% 100% / 0.14) 100%
|
||||||
|
),
|
||||||
|
var(--glass-bg);
|
||||||
|
backdrop-filter: blur(var(--glass-blur)) saturate(145%);
|
||||||
|
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(145%);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 0 var(--glass-border-highlight),
|
||||||
|
inset 0 0 0 1px hsla(0 0% 100% / 0.04),
|
||||||
|
var(--glass-shadow);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* More opaque glass — used behind dense text or form sections */
|
||||||
|
.glass-solid {
|
||||||
|
background: linear-gradient(
|
||||||
|
145deg,
|
||||||
|
hsla(0 0% 100% / 0.18) 0%,
|
||||||
|
hsla(0 0% 100% / 0.04) 55%,
|
||||||
|
hsla(0 0% 100% / 0.1) 100%
|
||||||
|
),
|
||||||
|
var(--glass-bg-solid);
|
||||||
|
backdrop-filter: blur(var(--glass-blur-heavy)) saturate(125%);
|
||||||
|
-webkit-backdrop-filter: blur(var(--glass-blur-heavy)) saturate(125%);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 0 var(--glass-border-highlight),
|
||||||
|
inset 0 0 0 1px hsla(0 0% 100% / 0.04),
|
||||||
|
var(--glass-shadow);
|
||||||
|
border-radius: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrim gradient for text protection on glass surfaces */
|
||||||
|
.glass-scrim {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
var(--glass-bg-scrim) 0%,
|
||||||
|
color-mix(in oklab, var(--glass-bg-scrim) 85%, transparent) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode card sheen — cool tinted gradient instead of a white glare */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.glass {
|
||||||
|
background: linear-gradient(
|
||||||
|
145deg,
|
||||||
|
hsla(220 30% 26% / 0.35) 0%,
|
||||||
|
hsla(220 20% 12% / 0.12) 55%,
|
||||||
|
hsla(220 30% 22% / 0.28) 100%
|
||||||
|
),
|
||||||
|
var(--glass-bg);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 0 var(--glass-border-highlight),
|
||||||
|
inset 0 0 0 1px hsla(0 0% 100% / 0.05),
|
||||||
|
var(--glass-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-solid {
|
||||||
|
background: linear-gradient(
|
||||||
|
145deg,
|
||||||
|
hsla(220 30% 22% / 0.3) 0%,
|
||||||
|
hsla(220 20% 10% / 0.08) 55%,
|
||||||
|
hsla(220 30% 18% / 0.22) 100%
|
||||||
|
),
|
||||||
|
var(--glass-bg-solid);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 0 var(--glass-border-highlight),
|
||||||
|
inset 0 0 0 1px hsla(0 0% 100% / 0.05),
|
||||||
|
var(--glass-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fallback for browsers without backdrop-filter support */
|
||||||
|
@supports not (backdrop-filter: blur(12px)) {
|
||||||
|
.glass {
|
||||||
|
background: var(--glass-bg-solid);
|
||||||
|
backdrop-filter: none;
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-solid {
|
||||||
|
backdrop-filter: none;
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ambient background blobs.
|
||||||
|
* Huge, very soft, slowly drifting color clouds.
|
||||||
|
* The heavy blur makes them feel like liquid light.
|
||||||
|
*/
|
||||||
|
.glass-ambient-blob {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(90px);
|
||||||
|
opacity: 0.85;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
animation: glass-float 28s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.glass-ambient-blob {
|
||||||
|
animation: none;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glass-float {
|
||||||
|
0% {
|
||||||
|
transform: translate3d(0, 0, 0) scale(1) rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate3d(60px, -70px, 0) scale(1.22) rotate(12deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aurora-like flowing gradient bands.
|
||||||
|
* Long, elliptical, heavily blurred shapes that drift across the screen.
|
||||||
|
*/
|
||||||
|
.glass-ambient-aurora {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(110px);
|
||||||
|
opacity: 0.7;
|
||||||
|
will-change: transform;
|
||||||
|
animation: glass-aurora-drift 36s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.glass-ambient-aurora {
|
||||||
|
animation: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glass-aurora-drift {
|
||||||
|
0% {
|
||||||
|
transform: translate3d(0, 0, 0) scale(1) rotate(-6deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate3d(-80px, 40px, 0) scale(1.18) rotate(8deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subtle vignette overlay.
|
||||||
|
* Darkens the edges so the eye is drawn to the glass card in the center.
|
||||||
|
*/
|
||||||
|
.glass-vignette {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse at center,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 50%,
|
||||||
|
hsla(0 0% 0% / 0.06) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.glass-vignette {
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse at center,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 45%,
|
||||||
|
hsla(0 0% 0% / 0.35) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text stability helper */
|
||||||
|
.glass-text-stable {
|
||||||
|
text-shadow: var(--glass-text-shadow);
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { GlassAmbientBackground } from "./GlassAmbientBackground";
|
||||||
|
export { GlassCard } from "./GlassCard";
|
||||||
+339
-23
@@ -1,5 +1,6 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
|
@import "./glass/glass-theme.css";
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--font-sans: "Inter", "SF Pro Text", "SF Pro Display", "Helvetica Neue", "Segoe UI",
|
--font-sans: "Inter", "SF Pro Text", "SF Pro Display", "Helvetica Neue", "Segoe UI",
|
||||||
@@ -25,6 +26,19 @@
|
|||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
--color-ring: var(--ring);
|
--color-ring: var(--ring);
|
||||||
|
|
||||||
|
/* Glassmorphism tokens exposed to Tailwind */
|
||||||
|
--color-glass-bg: var(--glass-bg);
|
||||||
|
--color-glass-border: var(--glass-border);
|
||||||
|
--color-glass-shadow: var(--glass-shadow);
|
||||||
|
|
||||||
|
/* Alert/status tokens */
|
||||||
|
--color-info: var(--info);
|
||||||
|
--color-info-foreground: var(--info-foreground);
|
||||||
|
--color-success: var(--success);
|
||||||
|
--color-success-foreground: var(--success-foreground);
|
||||||
|
--color-warning: var(--warning);
|
||||||
|
--color-warning-foreground: var(--warning-foreground);
|
||||||
|
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
@@ -33,7 +47,7 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: hsl(0 0% 100%);
|
--background: hsl(220 30% 96%);
|
||||||
--foreground: hsl(222.2 84% 4.9%);
|
--foreground: hsl(222.2 84% 4.9%);
|
||||||
--card: hsl(0 0% 100%);
|
--card: hsl(0 0% 100%);
|
||||||
--card-foreground: hsl(222.2 84% 4.9%);
|
--card-foreground: hsl(222.2 84% 4.9%);
|
||||||
@@ -49,10 +63,66 @@
|
|||||||
--accent-foreground: hsl(222.2 47.4% 11.2%);
|
--accent-foreground: hsl(222.2 47.4% 11.2%);
|
||||||
--destructive: hsl(0 84.2% 60.2%);
|
--destructive: hsl(0 84.2% 60.2%);
|
||||||
--destructive-foreground: hsl(210 40% 98%);
|
--destructive-foreground: hsl(210 40% 98%);
|
||||||
--border: hsl(214.3 31.8% 91.4%);
|
/* Darker border for better visibility on light glass backgrounds */
|
||||||
--input: hsl(214.3 31.8% 91.4%);
|
--border: hsl(214.3 25% 78%);
|
||||||
|
--input: hsl(214.3 25% 78%);
|
||||||
--ring: hsl(222.2 84% 4.9%);
|
--ring: hsl(222.2 84% 4.9%);
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
|
|
||||||
|
/* Status colors */
|
||||||
|
--info: hsl(210 80% 52%);
|
||||||
|
--info-foreground: hsl(210 40% 98%);
|
||||||
|
--success: hsl(142 76% 36%);
|
||||||
|
--success-foreground: hsl(210 40% 98%);
|
||||||
|
--warning: hsl(38 92% 50%);
|
||||||
|
--warning-foreground: hsl(210 40% 98%);
|
||||||
|
|
||||||
|
/* Page gradient visible behind the glass card */
|
||||||
|
--page-bg-gradient: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
hsl(220 35% 93%) 0%,
|
||||||
|
hsl(250 30% 90%) 50%,
|
||||||
|
hsl(300 25% 92%) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: hsl(222 47% 6%);
|
||||||
|
--foreground: hsl(210 40% 98%);
|
||||||
|
--card: hsl(220 20% 12%);
|
||||||
|
--card-foreground: hsl(210 40% 98%);
|
||||||
|
--popover: hsl(220 20% 12%);
|
||||||
|
--popover-foreground: hsl(210 40% 98%);
|
||||||
|
--primary: hsl(210 40% 98%);
|
||||||
|
--primary-foreground: hsl(222.2 47.4% 11.2%);
|
||||||
|
--secondary: hsl(217.2 32.6% 17.5%);
|
||||||
|
--secondary-foreground: hsl(210 40% 98%);
|
||||||
|
--muted: hsl(217.2 32.6% 17.5%);
|
||||||
|
--muted-foreground: hsl(215 20.2% 65.1%);
|
||||||
|
--accent: hsl(217.2 32.6% 17.5%);
|
||||||
|
--accent-foreground: hsl(210 40% 98%);
|
||||||
|
--destructive: hsl(0 72% 58%);
|
||||||
|
--destructive-foreground: hsl(210 40% 98%);
|
||||||
|
/* Lighter border so dividers remain visible on dark glass backgrounds */
|
||||||
|
--border: hsl(217.2 25% 34%);
|
||||||
|
--input: hsl(217.2 25% 34%);
|
||||||
|
--ring: hsl(212.7 26.8% 83.9%);
|
||||||
|
|
||||||
|
--info: hsl(210 80% 62%);
|
||||||
|
--info-foreground: hsl(222 47% 6%);
|
||||||
|
--success: hsl(142 70% 50%);
|
||||||
|
--success-foreground: hsl(222 47% 6%);
|
||||||
|
--warning: hsl(38 92% 58%);
|
||||||
|
--warning-foreground: hsl(222 47% 6%);
|
||||||
|
|
||||||
|
--page-bg-gradient: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
hsl(222 47% 6%) 0%,
|
||||||
|
hsl(250 35% 8%) 50%,
|
||||||
|
hsl(300 30% 7%) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
@@ -67,7 +137,7 @@
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
color: var(--color-foreground);
|
color: var(--color-foreground);
|
||||||
background: var(--color-background);
|
background: var(--page-bg-gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -97,18 +167,21 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 460px;
|
max-width: 460px;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
border: 1px solid var(--color-border);
|
/* Background, border and shadow are provided by the .glass utility. */
|
||||||
background: var(--color-card);
|
|
||||||
box-shadow:
|
|
||||||
0 1px 3px 0 rgb(0 0 0 / 0.1),
|
|
||||||
0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.kcHeaderClass {
|
.kcHeaderClass {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.875rem;
|
}
|
||||||
color: var(--color-muted-foreground);
|
|
||||||
|
#kc-header-wrapper {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.3;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
color: var(--color-foreground);
|
||||||
|
text-shadow: var(--glass-text-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.kcFormHeaderClass h1,
|
.kcFormHeaderClass h1,
|
||||||
@@ -188,6 +261,77 @@
|
|||||||
color: var(--color-destructive);
|
color: var(--color-destructive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Password visibility toggle inside the input */
|
||||||
|
.kcInputGroup {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcInputGroup .kcInputClass,
|
||||||
|
.kcInputGroup input[type="password"],
|
||||||
|
.kcInputGroup input[type="text"] {
|
||||||
|
padding-right: 2.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityButtonClass {
|
||||||
|
position: absolute;
|
||||||
|
right: 1.125rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.125rem;
|
||||||
|
height: 1.125rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-muted-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityButtonClass:hover {
|
||||||
|
color: var(--color-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityButtonClass:hover .kcFormPasswordVisibilityIconShow,
|
||||||
|
.kcFormPasswordVisibilityButtonClass:hover .kcFormPasswordVisibilityIconHide {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityButtonClass:focus-visible {
|
||||||
|
outline: 2px solid var(--color-ring);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityIconShow,
|
||||||
|
.kcFormPasswordVisibilityIconHide {
|
||||||
|
display: inline-block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 0.875rem;
|
||||||
|
height: 0.875rem;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityIconShow {
|
||||||
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzY0NzQ4YiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cGF0aCBkPSJNMTIgNC41QzcgNC41IDIuNzMgNy42MSAxIDEyYzEuNzMgNC4zOSA2IDcuNSAxMSA3LjVzOS4yNy0zLjExIDExLTcuNUMyMS4yNyA3LjYxIDE3IDQuNSAxMiA0LjV6TTEyIDE3Yy0yLjc2IDAtNS0yLjI0LTUtNXMyLjI0LTUgNS01IDUgMi4yNCA1IDUtMi4yNCA1LTUgNXoiLz48L3N2Zz4=");
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityIconHide {
|
||||||
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzY0NzQ4YiI+PHBhdGggZD0iTTEyIDdjMi43NiAwIDUgMi4yNCA1IDUgMCAuNjUtLjEzIDEuMjYtLjM2IDEuODNsMi45MiAyLjkyYzEuNTEtMS4yNiAyLjctMi44OSAzLjQzLTQuNzUtMS43My00LjM5LTYtNy41LTExLTcuNS0xLjQgMC0yLjc0LjI1LTMuOTguN2wyLjE3IDIuMTdDMTAuNzQgNy4xMyAxMS4zNSA3IDEyIDd6TTIgNC4yN2wyLjI4IDIuMjguNDYuNDZDMy4wOCA4LjMgMS43OCAxMC4wMiAxIDEyYzEuNzMgNC4zOSA2IDcuNSAxMSA3LjUgMS41NSAwIDMuMDMtLjMgNC4zOC0uODRsLjQyLjQyTDE5LjczIDIyIDIxIDIwLjczIDMuMjcgMyAyIDQuMjd6TTcuNTMgOS44bDEuNTUgMS41NWMtLjA1LjIxLS4wOC40My0uMDguNjUgMCAxLjY2IDEuMzQgMyAzIDMgLjIyIDAgLjQ0LS4wMy42NS0uMDhsMS41NSAxLjU1Yy0uNjcuMzMtMS40MS41My0yLjIuNTMtMi43NiAwLTUtMi4yNC01LTUgMC0uNzkuMi0xLjUzLjUzLTIuMnptNC4zMS0uNzhsMy4xNSAzLjE1LjAyLS4xNmMwLTEuNjYtMS4zNC0zLTMtM2wtLjE3LjAxeiIvPjwvc3ZnPg==");
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.kcFormPasswordVisibilityIconShow {
|
||||||
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzk0YTNiOCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cGF0aCBkPSJNMTIgNC41QzcgNC41IDIuNzMgNy42MSAxIDEyYzEuNzMgNC4zOSA2IDcuNSAxMSA3LjVzOS4yNy0zLjExIDExLTcuNUMyMS4yNyA3LjYxIDE3IDQuNSAxMiA0LjV6TTEyIDE3Yy0yLjc2IDAtNS0yLjI0LTUtNXMyLjI0LTUgNS01IDUgMi4yNCA1IDUtMi4yNCA1LTUgNXoiLz48L3N2Zz4=");
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormPasswordVisibilityIconHide {
|
||||||
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzk0YTNiOCI+PHBhdGggZD0iTTEyIDdjMi43NiAwIDUgMi4yNCA1IDUgMCAuNjUtLjEzIDEuMjYtLjM2IDEuODNsMi45MiAyLjkyYzEuNTEtMS4yNiAyLjctMi44OSAzLjQzLTQuNzUtMS43My00LjM5LTYtNy41LTExLTcuNS0xLjQgMC0yLjc0LjI1LTMuOTguN2wyLjE3IDIuMTdDMTAuNzQgNy4xMyAxMS4zNSA3IDEyIDd6TTIgNC4yN2wyLjI4IDIuMjguNDYuNDZDMy4wOCA4LjMgMS43OCAxMC4wMiAxIDEyYzEuNzMgNC4zOSA2IDcuNSAxMSA3LjUgMS41NSAwIDMuMDMtLjMgNC4zOC0uODRsLjQyLjQyTDE5LjczIDIyIDIxIDIwLjczIDMuMjcgMyAyIDQuMjd6TTcuNTMgOS44bDEuNTUgMS41NWMtLjA1LjIxLS4wOC40My0uMDguNjUgMCAxLjY2IDEuMzQgMyAzIDMgLjIyIDAgLjQ0LS4wMy42NS0uMDhsMS41NSAxLjU1Yy0uNjcuMzMtMS40MS41My0yLjIuNTMtMi43NiAwLTUtMi4yNC01LTUgMC0uNzkuMi0xLjUzLjUzLTIuMnptNC4zMS0uNzhsMy4xNSAzLjE1LjAyLS4xNmMwLTEuNjYtMS4zNC0zLTMtM2wtLjE3LjAxeiIvPjwvc3ZnPg==");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.kcButtonClass,
|
.kcButtonClass,
|
||||||
button,
|
button,
|
||||||
input[type="submit"],
|
input[type="submit"],
|
||||||
@@ -233,8 +377,7 @@
|
|||||||
|
|
||||||
.kcButtonDefaultClass,
|
.kcButtonDefaultClass,
|
||||||
.kcButtonSecondaryClass,
|
.kcButtonSecondaryClass,
|
||||||
#cancel-aia,
|
#cancel-aia {
|
||||||
#try-another-way {
|
|
||||||
background: var(--color-secondary);
|
background: var(--color-secondary);
|
||||||
color: var(--color-secondary-foreground);
|
color: var(--color-secondary-foreground);
|
||||||
border-color: var(--color-border);
|
border-color: var(--color-border);
|
||||||
@@ -242,9 +385,41 @@
|
|||||||
|
|
||||||
.kcButtonDefaultClass:hover,
|
.kcButtonDefaultClass:hover,
|
||||||
.kcButtonSecondaryClass:hover,
|
.kcButtonSecondaryClass:hover,
|
||||||
#cancel-aia:hover,
|
#cancel-aia:hover {
|
||||||
|
background: color-mix(in oklab, var(--color-secondary) 90%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#try-another-way {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
text-decoration: none;
|
||||||
|
background: var(--color-secondary);
|
||||||
|
color: var(--color-secondary-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
color 150ms ease,
|
||||||
|
background-color 150ms ease,
|
||||||
|
border-color 150ms ease,
|
||||||
|
box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
#try-another-way:hover {
|
#try-another-way:hover {
|
||||||
background: color-mix(in oklab, var(--color-secondary) 90%, transparent);
|
background: color-mix(in oklab, var(--color-secondary) 90%, transparent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-select-try-another-way-form {
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#kc-form-buttons {
|
#kc-form-buttons {
|
||||||
@@ -255,6 +430,10 @@
|
|||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kcFormSettingClass #kc-form-buttons {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.kcFormSettingClass {
|
.kcFormSettingClass {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -264,11 +443,32 @@
|
|||||||
padding-top: 0.75rem;
|
padding-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#kc-form-options .checkbox {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-form-options .checkbox label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-form-options .checkbox label span {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.kcFormOptionsClass,
|
.kcFormOptionsClass,
|
||||||
#kc-info {
|
#kc-info {
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kcFormSettingClass .kcFormOptionsClass {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#kc-info {
|
#kc-info {
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
@@ -330,24 +530,59 @@
|
|||||||
#kc-current-locale-link {
|
#kc-current-locale-link {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.35rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--glass-border);
|
||||||
background: var(--color-background);
|
background: var(--glass-bg);
|
||||||
padding: 0 0.5rem;
|
box-shadow:
|
||||||
|
inset 0 1px 0 0 var(--glass-border-highlight),
|
||||||
|
0 1px 2px hsla(0 0% 0% / 0.05);
|
||||||
|
padding: 0 0.65rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--color-muted-foreground);
|
color: var(--color-foreground);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition:
|
transition:
|
||||||
border-color 150ms ease,
|
border-color 150ms ease,
|
||||||
background-color 150ms ease;
|
background-color 150ms ease,
|
||||||
|
box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-registration a,
|
||||||
|
.kcFormOptionsWrapperClass a {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-registration a:hover,
|
||||||
|
.kcFormOptionsWrapperClass a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kcFormSettingClass .kcFormOptionsWrapperClass {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-current-locale-link::after {
|
||||||
|
content: "";
|
||||||
|
width: 0.4rem;
|
||||||
|
height: 0.4rem;
|
||||||
|
border: solid currentColor;
|
||||||
|
border-width: 0 1.5px 1.5px 0;
|
||||||
|
transform: rotate(45deg) translateY(-2px);
|
||||||
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
#kc-current-locale-link:hover,
|
#kc-current-locale-link:hover,
|
||||||
#kc-current-locale-link[aria-expanded="true"] {
|
#kc-current-locale-link[aria-expanded="true"] {
|
||||||
background: var(--color-muted);
|
background: var(--glass-bg-solid);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 0 var(--glass-border-highlight),
|
||||||
|
0 2px 4px hsla(0 0% 0% / 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.kcLocaleListClass {
|
.kcLocaleListClass {
|
||||||
@@ -409,8 +644,85 @@
|
|||||||
.alert-error.kcAlertClass,
|
.alert-error.kcAlertClass,
|
||||||
.pf-m-danger.kcAlertClass {
|
.pf-m-danger.kcAlertClass {
|
||||||
border-color: var(--color-destructive);
|
border-color: var(--color-destructive);
|
||||||
background: color-mix(in oklab, var(--color-destructive) 10%, transparent);
|
background: color-mix(in oklab, var(--color-destructive) 18%, transparent);
|
||||||
color: var(--color-destructive);
|
color: var(--color-destructive);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-m-info.kcAlertClass {
|
||||||
|
border-color: var(--color-info);
|
||||||
|
background: color-mix(in oklab, var(--color-info) 16%, transparent);
|
||||||
|
color: var(--color-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-m-success.kcAlertClass {
|
||||||
|
border-color: var(--color-success);
|
||||||
|
background: color-mix(in oklab, var(--color-success) 12%, transparent);
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-m-warning.kcAlertClass {
|
||||||
|
border-color: var(--color-warning);
|
||||||
|
background: color-mix(in oklab, var(--color-warning) 12%, transparent);
|
||||||
|
color: var(--color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Info/error page message boxes.
|
||||||
|
Only the .instruction paragraph is highlighted; action links stay plain. */
|
||||||
|
#kc-info-message,
|
||||||
|
#kc-error-message {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-info-message .instruction,
|
||||||
|
#kc-error-message .instruction {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-left-width: 4px;
|
||||||
|
padding: 0.875rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-info-message .instruction {
|
||||||
|
border-color: var(--color-info);
|
||||||
|
border-left-color: var(--color-info);
|
||||||
|
background: color-mix(in oklab, var(--color-info) 10%, transparent);
|
||||||
|
color: var(--color-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-error-message .instruction {
|
||||||
|
border-color: var(--color-destructive);
|
||||||
|
border-left-color: var(--color-destructive);
|
||||||
|
background: color-mix(in oklab, var(--color-destructive) 14%, transparent);
|
||||||
|
color: var(--color-destructive);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
#kc-error-message .instruction {
|
||||||
|
background: color-mix(in oklab, var(--color-destructive) 24%, transparent);
|
||||||
|
color: var(--color-destructive-foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-info-message p:not(.instruction),
|
||||||
|
#kc-error-message p:not(.instruction) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-info-message a,
|
||||||
|
#kc-error-message a {
|
||||||
|
color: var(--color-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-info-message a:hover,
|
||||||
|
#kc-error-message a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kcFormSocialAccountListButtonClass {
|
.kcFormSocialAccountListButtonClass {
|
||||||
@@ -777,7 +1089,7 @@
|
|||||||
content: "";
|
content: "";
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: var(--color-border);
|
background: color-mix(in oklab, var(--color-border) 70%, var(--color-foreground));
|
||||||
}
|
}
|
||||||
|
|
||||||
.kcCommonLogoIdP {
|
.kcCommonLogoIdP {
|
||||||
@@ -823,6 +1135,10 @@
|
|||||||
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20fill%3D%22%23FC3F1D%22%20d%3D%22M16.376%2012.644L21%202h-3.842l-4.624%2010.644h3.842zM13.915%2024v-3.733c0-2.822-.352-3.64-1.407-5.988L6.933%202H3l7.124%2015.709V24h3.79z%22%2F%3E%3C%2Fsvg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20fill%3D%22%23FC3F1D%22%20d%3D%22M16.376%2012.644L21%202h-3.842l-4.624%2010.644h3.842zM13.915%2024v-3.733c0-2.822-.352-3.64-1.407-5.988L6.933%202H3l7.124%2015.709V24h3.79z%22%2F%3E%3C%2Fsvg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#social-yandex .kcCommonLogoIdP {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#social-yandex.kcFormSocialAccountListButtonClass::before {
|
#social-yandex.kcFormSocialAccountListButtonClass::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
Reference in New Issue
Block a user