@cosxai/ui 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/index.ts +0 -1
- package/src/primitives/Input.tsx +111 -22
- package/src/styles/index.css +0 -1
- package/src/styles/tokens.css +0 -94
- package/src/theme/ThemeProvider.tsx +7 -1
- package/src/theme/index.ts +1 -0
- package/src/theme/inline-script.ts +5 -1
- package/src/theme/types.ts +18 -1
- package/src/frutiger/GlossyOrb.tsx +0 -79
- package/src/frutiger/SkyBackdrop.tsx +0 -114
- package/src/frutiger/index.ts +0 -2
- package/src/styles/chrome-frutiger.css +0 -364
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosxai/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "COSX design system — React 19 component primitives shared across product-meta and other consumers",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
"access": "public",
|
|
24
24
|
"registry": "https://registry.npmjs.org/"
|
|
25
25
|
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
26
29
|
"peerDependencies": {
|
|
27
30
|
"react": "^19.0.0",
|
|
28
31
|
"react-dom": "^19.0.0"
|
|
@@ -31,8 +34,5 @@
|
|
|
31
34
|
"@types/react": "^19.0.0",
|
|
32
35
|
"@types/react-dom": "^19.0.0",
|
|
33
36
|
"typescript": "^5.6.0"
|
|
34
|
-
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"typecheck": "tsc --noEmit"
|
|
37
37
|
}
|
|
38
|
-
}
|
|
38
|
+
}
|
package/src/index.ts
CHANGED
package/src/primitives/Input.tsx
CHANGED
|
@@ -4,8 +4,22 @@ import { cn } from "../lib/cn";
|
|
|
4
4
|
// Styled text input with optional label + error + helper text.
|
|
5
5
|
// Three states: idle / focused / invalid. Focus ring + invalid border
|
|
6
6
|
// both via :focus-visible / aria-invalid CSS — no JS branching.
|
|
7
|
+
//
|
|
8
|
+
// Optional `prefix` / `suffix` slots render INSIDE the bordered
|
|
9
|
+
// field, separated from the input by a subtle hairline divider.
|
|
10
|
+
// Common patterns:
|
|
11
|
+
// <Input prefix="https://" …/> → `https://[ acme ]`
|
|
12
|
+
// <Input suffix=".cosx.dev" …/> → `[ acme ].cosx.dev`
|
|
13
|
+
// Both can be set together. Prefer plain strings; ReactNode is
|
|
14
|
+
// supported (e.g. icons) but inherits the muted helper colour.
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
// We Omit "size" (HTMLInputElement's number-of-visible-chars attr;
|
|
17
|
+
// conflicts with our absent layout-size prop and is rarely needed in
|
|
18
|
+
// CSS-styled inputs) AND "prefix" (HTML's RDFa metadata attr —
|
|
19
|
+
// taking that prop name here lets us expose an addon slot with the
|
|
20
|
+
// most intuitive name; consumers needing the rare RDFa attr can
|
|
21
|
+
// drop down to a native <input>).
|
|
22
|
+
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "prefix"> {
|
|
9
23
|
label?: ReactNode;
|
|
10
24
|
helper?: ReactNode;
|
|
11
25
|
// Validation error message. When set, the input renders an
|
|
@@ -14,18 +28,72 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
|
|
|
14
28
|
error?: string | null;
|
|
15
29
|
// Width preset — full, fixed, or auto (default full).
|
|
16
30
|
fit?: "full" | "auto";
|
|
31
|
+
// Inline addons rendered inside the bordered field. Use for
|
|
32
|
+
// protocol prefixes ("https://"), domain suffixes (".cosx.dev"),
|
|
33
|
+
// currency symbols, unit labels, search icons, etc.
|
|
34
|
+
prefix?: ReactNode;
|
|
35
|
+
suffix?: ReactNode;
|
|
17
36
|
}
|
|
18
37
|
|
|
38
|
+
const ADDON_STYLE = {
|
|
39
|
+
display: "inline-flex",
|
|
40
|
+
alignItems: "center",
|
|
41
|
+
padding: "0 10px",
|
|
42
|
+
font: "400 13px/1 var(--ck-font-mono, var(--ck-font-sans))",
|
|
43
|
+
color: "var(--ck-text-tertiary)",
|
|
44
|
+
background: "var(--ck-bg-subtle, transparent)",
|
|
45
|
+
whiteSpace: "nowrap" as const,
|
|
46
|
+
userSelect: "none" as const,
|
|
47
|
+
};
|
|
48
|
+
|
|
19
49
|
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|
20
|
-
{ label, helper, error, fit = "full", className, id, ...rest },
|
|
50
|
+
{ label, helper, error, fit = "full", prefix, suffix, className, id, style, ...rest },
|
|
21
51
|
ref,
|
|
22
52
|
) {
|
|
23
53
|
const autoId = useId();
|
|
24
54
|
const inputId = id ?? autoId;
|
|
55
|
+
const hasAddon = prefix != null || suffix != null;
|
|
56
|
+
|
|
57
|
+
// Border / radius live on the WRAPPER when there's an addon so the
|
|
58
|
+
// addon + input share one continuous frame. Otherwise the input
|
|
59
|
+
// itself carries the border (preserves the prior visual contract).
|
|
60
|
+
const fieldBorder = `1px solid ${error ? "var(--ck-critical)" : "var(--ck-border-strong)"}`;
|
|
61
|
+
const fieldRadius = "var(--ck-radius-sm)";
|
|
62
|
+
|
|
63
|
+
const inputEl = (
|
|
64
|
+
<input
|
|
65
|
+
ref={ref}
|
|
66
|
+
id={inputId}
|
|
67
|
+
aria-invalid={error ? true : undefined}
|
|
68
|
+
aria-describedby={(helper || error) ? `${inputId}-helper` : undefined}
|
|
69
|
+
{...rest}
|
|
70
|
+
className={cn("ck-input", hasAddon ? "ck-input--with-addon" : undefined)}
|
|
71
|
+
style={{
|
|
72
|
+
flex: hasAddon ? "1 1 auto" : undefined,
|
|
73
|
+
minWidth: 0,
|
|
74
|
+
height: hasAddon ? "100%" : 34,
|
|
75
|
+
padding: "0 12px",
|
|
76
|
+
font: "400 13px/1 var(--ck-font-sans)",
|
|
77
|
+
background: "var(--ck-bg-surface)",
|
|
78
|
+
color: "var(--ck-text-primary)",
|
|
79
|
+
border: hasAddon ? "none" : fieldBorder,
|
|
80
|
+
borderRadius: hasAddon ? 0 : fieldRadius,
|
|
81
|
+
outline: "none",
|
|
82
|
+
transition: "border-color var(--ck-dur-fast) var(--ck-ease)",
|
|
83
|
+
...(style ?? {}),
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
|
|
25
88
|
return (
|
|
26
89
|
<div
|
|
27
90
|
className={cn("ck-input-field", className)}
|
|
28
|
-
style={{
|
|
91
|
+
style={{
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
gap: 6,
|
|
95
|
+
width: fit === "full" ? "100%" : undefined,
|
|
96
|
+
}}
|
|
29
97
|
>
|
|
30
98
|
{label && (
|
|
31
99
|
<label
|
|
@@ -36,25 +104,46 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|
|
36
104
|
{label}
|
|
37
105
|
</label>
|
|
38
106
|
)}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
107
|
+
{hasAddon ? (
|
|
108
|
+
<div
|
|
109
|
+
className="ck-input-addon-wrap"
|
|
110
|
+
style={{
|
|
111
|
+
display: "flex",
|
|
112
|
+
alignItems: "stretch",
|
|
113
|
+
height: 34,
|
|
114
|
+
background: "var(--ck-bg-surface)",
|
|
115
|
+
border: fieldBorder,
|
|
116
|
+
borderRadius: fieldRadius,
|
|
117
|
+
overflow: "hidden",
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{prefix != null && (
|
|
121
|
+
<span
|
|
122
|
+
className="ck-input-addon ck-input-addon--prefix"
|
|
123
|
+
style={{
|
|
124
|
+
...ADDON_STYLE,
|
|
125
|
+
borderRight: "1px solid var(--ck-border-subtle)",
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
{prefix}
|
|
129
|
+
</span>
|
|
130
|
+
)}
|
|
131
|
+
{inputEl}
|
|
132
|
+
{suffix != null && (
|
|
133
|
+
<span
|
|
134
|
+
className="ck-input-addon ck-input-addon--suffix"
|
|
135
|
+
style={{
|
|
136
|
+
...ADDON_STYLE,
|
|
137
|
+
borderLeft: "1px solid var(--ck-border-subtle)",
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
{suffix}
|
|
141
|
+
</span>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
) : (
|
|
145
|
+
inputEl
|
|
146
|
+
)}
|
|
58
147
|
{(helper || error) && (
|
|
59
148
|
<div
|
|
60
149
|
id={`${inputId}-helper`}
|
package/src/styles/index.css
CHANGED
package/src/styles/tokens.css
CHANGED
|
@@ -683,100 +683,6 @@ html[data-ck-theme="dark"][data-ck-chrome="bento"] {
|
|
|
683
683
|
top-highlight on every elevated element, soft blue-
|
|
684
684
|
tinted shadows + outer glow on hover. Image-driven,
|
|
685
685
|
optimistic, intentionally pretty. */
|
|
686
|
-
html[data-ck-chrome="frutiger"] {
|
|
687
|
-
/* Sky gradient as canvas — the whole page sits on it. */
|
|
688
|
-
--ck-bg-canvas: linear-gradient(180deg, #87CEEB 0%, #B3E5FC 45%, #E1F5FE 100%);
|
|
689
|
-
--ck-bg-sidebar: rgba(255, 255, 255, 0.55);
|
|
690
|
-
--ck-bg-surface: rgba(255, 255, 255, 0.62);
|
|
691
|
-
--ck-bg-surface-2: rgba(255, 255, 255, 0.78);
|
|
692
|
-
--ck-bg-muted: rgba(255, 255, 255, 0.4);
|
|
693
|
-
|
|
694
|
-
/* Cool, near-white borders with a hint of sky — they read as
|
|
695
|
-
light catching the edge of glass. */
|
|
696
|
-
--ck-border-subtle: rgba(255, 255, 255, 0.55);
|
|
697
|
-
--ck-border-strong: rgba(26, 35, 126, 0.18);
|
|
698
|
-
--ck-border-focus: var(--ck-accent);
|
|
699
|
-
|
|
700
|
-
/* Deep navy ink — black is too harsh against sky. */
|
|
701
|
-
--ck-text-primary: #0E1A6B;
|
|
702
|
-
--ck-text-secondary: #38507A;
|
|
703
|
-
--ck-text-tertiary: #6C82A8;
|
|
704
|
-
--ck-text-disabled: #B7C6DD;
|
|
705
|
-
--ck-text-inverse: #FFFFFF;
|
|
706
|
-
|
|
707
|
-
/* Aqua accent — the canonical Aero blue. */
|
|
708
|
-
--ck-accent: var(--ck-accent-light-override, #0288D1);
|
|
709
|
-
--ck-accent-hover: #01579B;
|
|
710
|
-
--ck-accent-muted: color-mix(in oklab, var(--ck-accent) 14%, transparent);
|
|
711
|
-
--ck-accent-border: color-mix(in oklab, var(--ck-accent) 38%, transparent);
|
|
712
|
-
|
|
713
|
-
/* Lime pop — used very sparingly for "go" / new / on-states. */
|
|
714
|
-
--ck-frutiger-lime: #C6FF00;
|
|
715
|
-
--ck-frutiger-orange: #FF9800;
|
|
716
|
-
|
|
717
|
-
/* Glossy gradient stacks — applied to buttons via background-
|
|
718
|
-
image. Two layers: the aqua tint at the bottom, the white
|
|
719
|
-
gloss highlight on the top half. */
|
|
720
|
-
--ck-frutiger-btn: linear-gradient(180deg, #4FC3F7 0%, #0288D1 50%, #01579B 100%);
|
|
721
|
-
--ck-frutiger-btn-hover: linear-gradient(180deg, #81D4FA 0%, #039BE5 50%, #0277BD 100%);
|
|
722
|
-
--ck-frutiger-gloss: linear-gradient(180deg, rgba(255,255,255,0.65) 0%, rgba(255,255,255,0) 50%, rgba(255,255,255,0) 100%);
|
|
723
|
-
|
|
724
|
-
/* Generous "pebble" radii. */
|
|
725
|
-
--ck-radius-xs: 8px;
|
|
726
|
-
--ck-radius-sm: 12px;
|
|
727
|
-
--ck-radius-md: 16px;
|
|
728
|
-
--ck-radius-lg: 20px;
|
|
729
|
-
--ck-radius-xl: 28px;
|
|
730
|
-
|
|
731
|
-
/* Soft blue-tinted shadows — never dark/crisp. */
|
|
732
|
-
--ck-shadow-1: 0 2px 8px rgba(0, 100, 200, 0.12);
|
|
733
|
-
--ck-shadow-2: 0 4px 16px rgba(0, 100, 200, 0.18), 0 1px 2px rgba(0, 100, 200, 0.08);
|
|
734
|
-
--ck-shadow-3: 0 12px 40px rgba(0, 100, 200, 0.28), 0 2px 6px rgba(0, 100, 200, 0.14);
|
|
735
|
-
|
|
736
|
-
/* The "Aero glow" — used on hover/focus. */
|
|
737
|
-
--ck-frutiger-glow: 0 0 24px rgba(79, 195, 247, 0.55);
|
|
738
|
-
--ck-frutiger-glow-soft: 0 0 14px rgba(79, 195, 247, 0.35);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
html[data-ck-theme="dark"][data-ck-chrome="frutiger"] {
|
|
742
|
-
/* Twilight / aurora sky for dark — the brief lives in light,
|
|
743
|
-
but a coherent dark mode keeps the chrome usable. Deep
|
|
744
|
-
navy → indigo → soft lavender gradient. Glass surfaces
|
|
745
|
-
become darker translucent panels. */
|
|
746
|
-
--ck-bg-canvas: linear-gradient(180deg, #0B1029 0%, #1A2167 50%, #2E2280 100%);
|
|
747
|
-
--ck-bg-sidebar: rgba(20, 30, 80, 0.55);
|
|
748
|
-
--ck-bg-surface: rgba(30, 45, 110, 0.55);
|
|
749
|
-
--ck-bg-surface-2: rgba(45, 60, 130, 0.65);
|
|
750
|
-
--ck-bg-muted: rgba(20, 30, 80, 0.4);
|
|
751
|
-
|
|
752
|
-
--ck-border-subtle: rgba(255, 255, 255, 0.16);
|
|
753
|
-
--ck-border-strong: rgba(255, 255, 255, 0.28);
|
|
754
|
-
|
|
755
|
-
--ck-text-primary: #EAF3FF;
|
|
756
|
-
--ck-text-secondary: #B7C6E8;
|
|
757
|
-
--ck-text-tertiary: #8094BE;
|
|
758
|
-
--ck-text-disabled: #4B5C84;
|
|
759
|
-
--ck-text-inverse: #0E1A6B;
|
|
760
|
-
|
|
761
|
-
--ck-accent: var(--ck-accent-dark-override, var(--ck-accent-light-override, #4FC3F7));
|
|
762
|
-
|
|
763
|
-
--ck-frutiger-btn: linear-gradient(180deg, #4FC3F7 0%, #0288D1 50%, #01579B 100%);
|
|
764
|
-
--ck-frutiger-btn-hover: linear-gradient(180deg, #81D4FA 0%, #039BE5 50%, #0277BD 100%);
|
|
765
|
-
|
|
766
|
-
--ck-shadow-1: 0 2px 8px rgba(0, 0, 0, 0.35);
|
|
767
|
-
--ck-shadow-2: 0 4px 16px rgba(0, 0, 0, 0.42), 0 1px 2px rgba(0, 0, 0, 0.32);
|
|
768
|
-
--ck-shadow-3: 0 12px 40px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.32);
|
|
769
|
-
|
|
770
|
-
--ck-frutiger-glow: 0 0 24px rgba(129, 212, 250, 0.45);
|
|
771
|
-
--ck-frutiger-glow-soft: 0 0 14px rgba(129, 212, 250, 0.3);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/* ---------- Risograph / Zine Print chrome — DIY print culture as
|
|
775
|
-
a digital aesthetic. Warm uncoated paper canvas, 2-3
|
|
776
|
-
fluorescent inks per composition (pink/blue/yellow
|
|
777
|
-
signature trio), halftone dots, misregistration
|
|
778
|
-
offset, hand-drawn imperfection, bold display type
|
|
779
|
-
+ workhorse mono body. Anti-glossy, anti-gradient. */
|
|
780
686
|
html[data-ck-chrome="riso"] {
|
|
781
687
|
/* Warm cream paper, never flat white. */
|
|
782
688
|
--ck-bg-canvas: #F4EFE5;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ReactNode,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { ThemeContext } from "./theme-context";
|
|
9
|
+
import { BUILTIN_CHROMES } from "./types";
|
|
9
10
|
import type { Theme, Chrome, ThemeContextValue } from "./types";
|
|
10
11
|
|
|
11
12
|
const DEFAULT_THEME_KEY = "ck-theme";
|
|
@@ -63,7 +64,12 @@ export function ThemeProvider({
|
|
|
63
64
|
return stored ?? preferredTheme(defaultTheme);
|
|
64
65
|
});
|
|
65
66
|
const [chrome, setChromeState] = useState<Chrome>(() => {
|
|
66
|
-
|
|
67
|
+
// Allow every built-in chrome out of localStorage. Custom strings
|
|
68
|
+
// (the `(string & {})` half of the Chrome union) are not
|
|
69
|
+
// persisted across reloads — consumers that ship custom chromes
|
|
70
|
+
// can opt-in via a separate localStorage write upstream of this
|
|
71
|
+
// provider if they want persistence.
|
|
72
|
+
const stored = readStored<Chrome>(chromeStorageKey, BUILTIN_CHROMES);
|
|
67
73
|
return stored ?? defaultChrome;
|
|
68
74
|
});
|
|
69
75
|
|
package/src/theme/index.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { ThemeProvider } from "./ThemeProvider";
|
|
|
2
2
|
export type { ThemeProviderProps } from "./ThemeProvider";
|
|
3
3
|
export { useTheme } from "./useTheme";
|
|
4
4
|
export { getInlineThemeScript } from "./inline-script";
|
|
5
|
+
export { BUILTIN_CHROMES } from "./types";
|
|
5
6
|
export type { Theme, Chrome, ThemeContextValue } from "./types";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BUILTIN_CHROMES } from "./types";
|
|
1
2
|
import type { Theme, Chrome } from "./types";
|
|
2
3
|
|
|
3
4
|
// Pre-mount inline script generator. Paste the returned string
|
|
@@ -22,6 +23,9 @@ export function getInlineThemeScript(opts: {
|
|
|
22
23
|
const c = JSON.stringify(opts.chromeStorageKey ?? "ck-chrome");
|
|
23
24
|
const dt = JSON.stringify(opts.defaultTheme ?? "system");
|
|
24
25
|
const dc = JSON.stringify(opts.defaultChrome ?? "seamless");
|
|
26
|
+
// Inline the allowlist so the runtime check is a single
|
|
27
|
+
// Array.indexOf — matches ThemeProvider's readStored behaviour.
|
|
28
|
+
const allowed = JSON.stringify(BUILTIN_CHROMES);
|
|
25
29
|
return `(function(){try{
|
|
26
30
|
var t=localStorage.getItem(${t});
|
|
27
31
|
if(t!=="light"&&t!=="dark"){
|
|
@@ -30,7 +34,7 @@ export function getInlineThemeScript(opts: {
|
|
|
30
34
|
}
|
|
31
35
|
document.documentElement.setAttribute("data-ck-theme",t);
|
|
32
36
|
var c=localStorage.getItem(${c});
|
|
33
|
-
if(c
|
|
37
|
+
if(${allowed}.indexOf(c)<0)c=${dc};
|
|
34
38
|
document.documentElement.setAttribute("data-ck-chrome",c);
|
|
35
39
|
}catch(e){}})();`;
|
|
36
40
|
}
|
package/src/theme/types.ts
CHANGED
|
@@ -6,12 +6,29 @@
|
|
|
6
6
|
|
|
7
7
|
export type Theme = "light" | "dark";
|
|
8
8
|
|
|
9
|
+
// BUILTIN_CHROMES is the source of truth for "chromes the kit ships
|
|
10
|
+
// with". The Chrome union below, the ThemeProvider localStorage
|
|
11
|
+
// validator, and the pre-mount inline script all key off this
|
|
12
|
+
// constant so adding (or removing) a chrome is a one-place change.
|
|
13
|
+
export const BUILTIN_CHROMES = [
|
|
14
|
+
"classic",
|
|
15
|
+
"seamless",
|
|
16
|
+
"editorial",
|
|
17
|
+
"neobrutalism",
|
|
18
|
+
"ambient",
|
|
19
|
+
"swiss",
|
|
20
|
+
"terminal",
|
|
21
|
+
"bento",
|
|
22
|
+
"riso",
|
|
23
|
+
"sketch",
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
9
26
|
// Built-in chromes ship with the kit; custom strings are also
|
|
10
27
|
// allowed at runtime (the kit just stamps the value onto
|
|
11
28
|
// `data-ck-chrome`, your CSS supplies the overrides). The
|
|
12
29
|
// `(string & {})` trick preserves autocomplete for the built-ins
|
|
13
30
|
// while widening the union to any string for custom chromes.
|
|
14
|
-
export type Chrome =
|
|
31
|
+
export type Chrome = (typeof BUILTIN_CHROMES)[number] | (string & {});
|
|
15
32
|
|
|
16
33
|
export interface ThemeContextValue {
|
|
17
34
|
theme: Theme;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties } from "react";
|
|
2
|
-
|
|
3
|
-
// Pure CSS 3D glass orb — the "Aero" / Vista / iOS 6 marble.
|
|
4
|
-
// Layered radial gradients: deep accent base, top white highlight,
|
|
5
|
-
// bottom internal reflection, soft outer glow. Drop it anywhere
|
|
6
|
-
// you want a hero / empty-state decoration.
|
|
7
|
-
|
|
8
|
-
export interface GlossyOrbProps {
|
|
9
|
-
size?: number;
|
|
10
|
-
// Tint of the orb. Defaults to aqua (Aero blue). Pass any color.
|
|
11
|
-
tone?: "aqua" | "lime" | "rose" | "amber";
|
|
12
|
-
// Disable the slow rotation animation.
|
|
13
|
-
static?: boolean;
|
|
14
|
-
className?: string;
|
|
15
|
-
style?: CSSProperties;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const TONES: Record<NonNullable<GlossyOrbProps["tone"]>, { base: string; deep: string; glow: string }> = {
|
|
19
|
-
aqua: { base: "#4FC3F7", deep: "#01579B", glow: "rgba(79, 195, 247, 0.55)" },
|
|
20
|
-
lime: { base: "#DCFF66", deep: "#7CB342", glow: "rgba(198, 255, 0, 0.55)" },
|
|
21
|
-
rose: { base: "#F48FB1", deep: "#AD1457", glow: "rgba(244, 143, 177, 0.55)" },
|
|
22
|
-
amber: { base: "#FFCA28", deep: "#E65100", glow: "rgba(255, 152, 0, 0.55)" },
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export function GlossyOrb({
|
|
26
|
-
size = 96,
|
|
27
|
-
tone = "aqua",
|
|
28
|
-
static: noSpin,
|
|
29
|
-
className,
|
|
30
|
-
style,
|
|
31
|
-
}: GlossyOrbProps) {
|
|
32
|
-
const t = TONES[tone];
|
|
33
|
-
return (
|
|
34
|
-
<div
|
|
35
|
-
className={className}
|
|
36
|
-
aria-hidden
|
|
37
|
-
style={{
|
|
38
|
-
width: size,
|
|
39
|
-
height: size,
|
|
40
|
-
borderRadius: "50%",
|
|
41
|
-
position: "relative",
|
|
42
|
-
background: `
|
|
43
|
-
radial-gradient(circle at 50% 28%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.0) 32%),
|
|
44
|
-
radial-gradient(circle at 50% 95%, ${t.base} 0%, transparent 40%),
|
|
45
|
-
radial-gradient(circle at 50% 50%, ${t.base} 0%, ${t.deep} 100%)
|
|
46
|
-
`,
|
|
47
|
-
boxShadow: `
|
|
48
|
-
0 ${size * 0.12}px ${size * 0.28}px ${t.glow},
|
|
49
|
-
inset 0 ${size * 0.08}px ${size * 0.12}px rgba(255, 255, 255, 0.55),
|
|
50
|
-
inset 0 -${size * 0.08}px ${size * 0.16}px rgba(0, 0, 0, 0.22)
|
|
51
|
-
`,
|
|
52
|
-
animation: noSpin ? undefined : "ck-frutiger-orb-spin 18s linear infinite",
|
|
53
|
-
...style,
|
|
54
|
-
}}
|
|
55
|
-
>
|
|
56
|
-
{/* Specular highlight — small bright cap top-left */}
|
|
57
|
-
<span
|
|
58
|
-
style={{
|
|
59
|
-
position: "absolute",
|
|
60
|
-
top: size * 0.1,
|
|
61
|
-
left: size * 0.22,
|
|
62
|
-
width: size * 0.36,
|
|
63
|
-
height: size * 0.22,
|
|
64
|
-
borderRadius: "50%",
|
|
65
|
-
background: "radial-gradient(ellipse, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 70%)",
|
|
66
|
-
}}
|
|
67
|
-
/>
|
|
68
|
-
<style>{`
|
|
69
|
-
@keyframes ck-frutiger-orb-spin {
|
|
70
|
-
0% { transform: rotate(0deg); }
|
|
71
|
-
100% { transform: rotate(360deg); }
|
|
72
|
-
}
|
|
73
|
-
@media (prefers-reduced-motion: reduce) {
|
|
74
|
-
[data-ck-glossy-orb] { animation: none !important; }
|
|
75
|
-
}
|
|
76
|
-
`}</style>
|
|
77
|
-
</div>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
// Drop-in section backdrop. The frutiger chrome already paints the
|
|
4
|
-
// page with a sky gradient — this component is for opt-in moments
|
|
5
|
-
// inside other chromes (a hero banner under chrome="classic", a
|
|
6
|
-
// preview area in the theming page) without flipping the whole app.
|
|
7
|
-
|
|
8
|
-
export interface SkyBackdropProps {
|
|
9
|
-
children?: ReactNode;
|
|
10
|
-
// Add the floating sparkle particles. Defaults to true.
|
|
11
|
-
sparkles?: boolean;
|
|
12
|
-
// Add soft white cumulus blobs near the bottom. Defaults to true.
|
|
13
|
-
clouds?: boolean;
|
|
14
|
-
// Variant: dusk swaps the gradient for indigo→lavender→pink.
|
|
15
|
-
variant?: "day" | "dusk";
|
|
16
|
-
className?: string;
|
|
17
|
-
style?: CSSProperties;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const DAY_BG = "linear-gradient(180deg, #87CEEB 0%, #B3E5FC 45%, #E1F5FE 100%)";
|
|
21
|
-
const DUSK_BG = "linear-gradient(180deg, #2E2280 0%, #6A4C93 40%, #F4A6CD 80%, #FFD8B1 100%)";
|
|
22
|
-
|
|
23
|
-
export function SkyBackdrop({
|
|
24
|
-
children,
|
|
25
|
-
sparkles = true,
|
|
26
|
-
clouds = true,
|
|
27
|
-
variant = "day",
|
|
28
|
-
className,
|
|
29
|
-
style,
|
|
30
|
-
}: SkyBackdropProps) {
|
|
31
|
-
return (
|
|
32
|
-
<div
|
|
33
|
-
className={className}
|
|
34
|
-
style={{
|
|
35
|
-
position: "relative",
|
|
36
|
-
overflow: "hidden",
|
|
37
|
-
background: variant === "dusk" ? DUSK_BG : DAY_BG,
|
|
38
|
-
...style,
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
{clouds && <Clouds variant={variant} />}
|
|
42
|
-
{sparkles && <Sparkles />}
|
|
43
|
-
<div style={{ position: "relative", zIndex: 2 }}>{children}</div>
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function Clouds({ variant }: { variant: "day" | "dusk" }) {
|
|
49
|
-
const tint = variant === "dusk" ? "rgba(255, 220, 230, 0.5)" : "rgba(255, 255, 255, 0.6)";
|
|
50
|
-
return (
|
|
51
|
-
<div
|
|
52
|
-
aria-hidden
|
|
53
|
-
style={{
|
|
54
|
-
position: "absolute",
|
|
55
|
-
inset: 0,
|
|
56
|
-
background: `
|
|
57
|
-
radial-gradient(ellipse 280px 80px at 14% 78%, ${tint} 0%, transparent 70%),
|
|
58
|
-
radial-gradient(ellipse 360px 90px at 72% 86%, ${tint} 0%, transparent 70%),
|
|
59
|
-
radial-gradient(ellipse 200px 60px at 50% 92%, ${tint} 0%, transparent 70%)
|
|
60
|
-
`,
|
|
61
|
-
pointerEvents: "none",
|
|
62
|
-
zIndex: 1,
|
|
63
|
-
}}
|
|
64
|
-
/>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function Sparkles() {
|
|
69
|
-
// 14 deterministic positions — random feels jittery on reload.
|
|
70
|
-
const dots = [
|
|
71
|
-
{ x: 8, y: 22, s: 3, o: 0.85 },
|
|
72
|
-
{ x: 18, y: 12, s: 2, o: 0.65 },
|
|
73
|
-
{ x: 31, y: 32, s: 4, o: 0.9 },
|
|
74
|
-
{ x: 44, y: 18, s: 2, o: 0.55 },
|
|
75
|
-
{ x: 56, y: 42, s: 3, o: 0.7 },
|
|
76
|
-
{ x: 68, y: 14, s: 2, o: 0.6 },
|
|
77
|
-
{ x: 76, y: 30, s: 5, o: 0.8 },
|
|
78
|
-
{ x: 88, y: 24, s: 2, o: 0.55 },
|
|
79
|
-
{ x: 22, y: 56, s: 3, o: 0.55 },
|
|
80
|
-
{ x: 38, y: 64, s: 2, o: 0.5 },
|
|
81
|
-
{ x: 60, y: 60, s: 4, o: 0.7 },
|
|
82
|
-
{ x: 82, y: 50, s: 2, o: 0.55 },
|
|
83
|
-
{ x: 12, y: 42, s: 2, o: 0.5 },
|
|
84
|
-
{ x: 50, y: 28, s: 2, o: 0.7 },
|
|
85
|
-
];
|
|
86
|
-
return (
|
|
87
|
-
<div
|
|
88
|
-
aria-hidden
|
|
89
|
-
style={{
|
|
90
|
-
position: "absolute",
|
|
91
|
-
inset: 0,
|
|
92
|
-
pointerEvents: "none",
|
|
93
|
-
zIndex: 1,
|
|
94
|
-
}}
|
|
95
|
-
>
|
|
96
|
-
{dots.map((d, i) => (
|
|
97
|
-
<span
|
|
98
|
-
key={i}
|
|
99
|
-
style={{
|
|
100
|
-
position: "absolute",
|
|
101
|
-
left: `${d.x}%`,
|
|
102
|
-
top: `${d.y}%`,
|
|
103
|
-
width: d.s,
|
|
104
|
-
height: d.s,
|
|
105
|
-
borderRadius: "50%",
|
|
106
|
-
background: "#FFFFFF",
|
|
107
|
-
opacity: d.o,
|
|
108
|
-
boxShadow: `0 0 ${d.s * 4}px ${d.s * 1.5}px rgba(255, 255, 255, ${d.o * 0.55})`,
|
|
109
|
-
}}
|
|
110
|
-
/>
|
|
111
|
-
))}
|
|
112
|
-
</div>
|
|
113
|
-
);
|
|
114
|
-
}
|
package/src/frutiger/index.ts
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
/* Frutiger Aero / Glossy Revival chrome — Aero / Vista / iOS 6 /
|
|
2
|
-
Vision Pro Liquid Glass. Glossy buttons with a two-layer gradient
|
|
3
|
-
(aqua base + white gloss top half), glass cards with backdrop-
|
|
4
|
-
filter, soft blue-tinted shadows, outer glow on hover, deep navy
|
|
5
|
-
ink. Sparkle particles + shimmer sweeps are opt-in via classnames
|
|
6
|
-
so the chrome only "performs" when you want it to.
|
|
7
|
-
|
|
8
|
-
Implementation note: the sky-gradient canvas is applied via the
|
|
9
|
-
tokens.css `--ck-bg-canvas`. The kit's base layer already does
|
|
10
|
-
`body { background: var(--ck-bg-canvas); }` so the gradient
|
|
11
|
-
value carries through. */
|
|
12
|
-
|
|
13
|
-
/* Body — humanist sans, deep navy, slightly tighter letter-spacing
|
|
14
|
-
on hero text. */
|
|
15
|
-
html[data-ck-chrome="frutiger"] body {
|
|
16
|
-
font-family: "Avenir Next", "Calibre", "SF Pro Rounded", var(--ck-font-sans);
|
|
17
|
-
background-attachment: fixed;
|
|
18
|
-
letter-spacing: -0.005em;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
html[data-ck-chrome="frutiger"] h1,
|
|
22
|
-
html[data-ck-chrome="frutiger"] .ck-h1 {
|
|
23
|
-
font-weight: 700;
|
|
24
|
-
letter-spacing: -0.01em;
|
|
25
|
-
line-height: 1.05;
|
|
26
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8),
|
|
27
|
-
0 2px 4px rgba(0, 100, 200, 0.18);
|
|
28
|
-
}
|
|
29
|
-
html[data-ck-chrome="frutiger"] h2,
|
|
30
|
-
html[data-ck-chrome="frutiger"] .ck-h2 {
|
|
31
|
-
font-weight: 600;
|
|
32
|
-
letter-spacing: -0.005em;
|
|
33
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
|
34
|
-
}
|
|
35
|
-
html[data-ck-chrome="frutiger"] h3,
|
|
36
|
-
html[data-ck-chrome="frutiger"] .ck-h3 {
|
|
37
|
-
font-weight: 600;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* Dark mode: drop the white text-shadow (would look like a bug
|
|
41
|
-
against the navy canvas). */
|
|
42
|
-
html[data-ck-theme="dark"][data-ck-chrome="frutiger"] h1,
|
|
43
|
-
html[data-ck-theme="dark"][data-ck-chrome="frutiger"] h2,
|
|
44
|
-
html[data-ck-theme="dark"][data-ck-chrome="frutiger"] h3 {
|
|
45
|
-
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/* ---------- Buttons — the signature glossy candy element ---------- */
|
|
49
|
-
html[data-ck-chrome="frutiger"] .ck-btn,
|
|
50
|
-
html[data-ck-chrome="frutiger"] .ck-actionbar-btn {
|
|
51
|
-
border-radius: 12px;
|
|
52
|
-
font-weight: 600;
|
|
53
|
-
letter-spacing: -0.005em;
|
|
54
|
-
height: 36px;
|
|
55
|
-
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
56
|
-
box-shadow:
|
|
57
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
|
58
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.2),
|
|
59
|
-
0 2px 8px rgba(0, 100, 200, 0.18);
|
|
60
|
-
transition:
|
|
61
|
-
box-shadow 200ms ease,
|
|
62
|
-
transform 200ms ease,
|
|
63
|
-
background 200ms ease;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
html[data-ck-chrome="frutiger"] .ck-btn--primary,
|
|
67
|
-
html[data-ck-chrome="frutiger"] .ck-actionbar-btn--primary {
|
|
68
|
-
background:
|
|
69
|
-
var(--ck-frutiger-gloss),
|
|
70
|
-
var(--ck-frutiger-btn);
|
|
71
|
-
color: #FFFFFF;
|
|
72
|
-
text-shadow: 0 1px 0 rgba(0, 50, 120, 0.45);
|
|
73
|
-
border-color: rgba(2, 88, 155, 0.7);
|
|
74
|
-
}
|
|
75
|
-
html[data-ck-chrome="frutiger"] .ck-btn--primary:hover:not(:disabled),
|
|
76
|
-
html[data-ck-chrome="frutiger"] .ck-actionbar-btn--primary:hover:not(:disabled) {
|
|
77
|
-
background:
|
|
78
|
-
var(--ck-frutiger-gloss),
|
|
79
|
-
var(--ck-frutiger-btn-hover);
|
|
80
|
-
box-shadow:
|
|
81
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.75),
|
|
82
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.2),
|
|
83
|
-
var(--ck-frutiger-glow);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
html[data-ck-chrome="frutiger"] .ck-btn--secondary {
|
|
87
|
-
background:
|
|
88
|
-
linear-gradient(180deg, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0.35) 50%, rgba(255,255,255,0.55) 100%);
|
|
89
|
-
color: var(--ck-text-primary);
|
|
90
|
-
border-color: rgba(255, 255, 255, 0.65);
|
|
91
|
-
backdrop-filter: blur(8px);
|
|
92
|
-
}
|
|
93
|
-
html[data-ck-chrome="frutiger"] .ck-btn--secondary:hover:not(:disabled) {
|
|
94
|
-
box-shadow:
|
|
95
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.8),
|
|
96
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.18),
|
|
97
|
-
var(--ck-frutiger-glow-soft);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
html[data-ck-chrome="frutiger"] .ck-btn--ghost {
|
|
101
|
-
background: rgba(255, 255, 255, 0.0);
|
|
102
|
-
border-color: transparent;
|
|
103
|
-
box-shadow: none;
|
|
104
|
-
color: var(--ck-text-primary);
|
|
105
|
-
}
|
|
106
|
-
html[data-ck-chrome="frutiger"] .ck-btn--ghost:hover:not(:disabled) {
|
|
107
|
-
background: rgba(255, 255, 255, 0.35);
|
|
108
|
-
border-color: rgba(255, 255, 255, 0.45);
|
|
109
|
-
box-shadow:
|
|
110
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
|
111
|
-
0 1px 4px rgba(0, 100, 200, 0.14);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
html[data-ck-chrome="frutiger"] .ck-btn:active:not(:disabled),
|
|
115
|
-
html[data-ck-chrome="frutiger"] .ck-actionbar-btn:active:not(:disabled) {
|
|
116
|
-
transform: translateY(1px);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/* ---------- Inputs — inset glass, glow on focus ---------- */
|
|
120
|
-
html[data-ck-chrome="frutiger"] .ck-input,
|
|
121
|
-
html[data-ck-chrome="frutiger"] .ck-textarea {
|
|
122
|
-
background:
|
|
123
|
-
linear-gradient(180deg, rgba(0, 50, 120, 0.06) 0%, rgba(255, 255, 255, 0.6) 18%);
|
|
124
|
-
border: 1px solid rgba(0, 60, 140, 0.18);
|
|
125
|
-
border-radius: 12px;
|
|
126
|
-
box-shadow:
|
|
127
|
-
inset 0 2px 4px rgba(0, 50, 120, 0.12),
|
|
128
|
-
0 1px 0 rgba(255, 255, 255, 0.85);
|
|
129
|
-
color: var(--ck-text-primary);
|
|
130
|
-
}
|
|
131
|
-
html[data-ck-chrome="frutiger"] .ck-input::placeholder,
|
|
132
|
-
html[data-ck-chrome="frutiger"] .ck-textarea::placeholder {
|
|
133
|
-
color: rgba(56, 80, 122, 0.55);
|
|
134
|
-
}
|
|
135
|
-
html[data-ck-chrome="frutiger"] .ck-input:focus-visible,
|
|
136
|
-
html[data-ck-chrome="frutiger"] .ck-textarea:focus-visible {
|
|
137
|
-
outline: none;
|
|
138
|
-
border-color: var(--ck-accent) !important;
|
|
139
|
-
box-shadow:
|
|
140
|
-
inset 0 2px 4px rgba(0, 50, 120, 0.12),
|
|
141
|
-
var(--ck-frutiger-glow-soft) !important;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/* ---------- Cards — glass plate with top highlight + bottom shadow line */
|
|
145
|
-
html[data-ck-chrome="frutiger"] .ck-card {
|
|
146
|
-
background:
|
|
147
|
-
linear-gradient(180deg, rgba(255,255,255,0.78) 0%, rgba(255,255,255,0.55) 100%);
|
|
148
|
-
backdrop-filter: blur(20px) saturate(140%);
|
|
149
|
-
-webkit-backdrop-filter: blur(20px) saturate(140%);
|
|
150
|
-
border: 1px solid rgba(255, 255, 255, 0.6);
|
|
151
|
-
border-radius: 18px;
|
|
152
|
-
box-shadow:
|
|
153
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.85),
|
|
154
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.14),
|
|
155
|
-
var(--ck-shadow-2);
|
|
156
|
-
position: relative;
|
|
157
|
-
}
|
|
158
|
-
html[data-ck-chrome="frutiger"] .ck-card__head {
|
|
159
|
-
background: transparent;
|
|
160
|
-
border-bottom: 1px solid rgba(0, 50, 120, 0.1);
|
|
161
|
-
padding: 14px 20px;
|
|
162
|
-
}
|
|
163
|
-
html[data-ck-chrome="frutiger"] .ck-card__foot {
|
|
164
|
-
background: transparent;
|
|
165
|
-
border-top: 1px solid rgba(0, 50, 120, 0.08);
|
|
166
|
-
padding: 12px 20px;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/* ---------- Tags — glossy pills ---------- */
|
|
170
|
-
html[data-ck-chrome="frutiger"] [data-ck-tag] {
|
|
171
|
-
border-radius: 999px !important;
|
|
172
|
-
padding: 3px 10px !important;
|
|
173
|
-
font-weight: 600 !important;
|
|
174
|
-
font-size: 11px !important;
|
|
175
|
-
letter-spacing: 0 !important;
|
|
176
|
-
text-transform: none !important;
|
|
177
|
-
border: 1px solid rgba(255, 255, 255, 0.5) !important;
|
|
178
|
-
box-shadow:
|
|
179
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.7),
|
|
180
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.12),
|
|
181
|
-
0 1px 3px rgba(0, 100, 200, 0.14) !important;
|
|
182
|
-
background:
|
|
183
|
-
linear-gradient(180deg, rgba(255,255,255,0.75) 0%, rgba(255,255,255,0.35) 100%) !important;
|
|
184
|
-
color: var(--ck-text-primary) !important;
|
|
185
|
-
}
|
|
186
|
-
html[data-ck-chrome="frutiger"] [data-ck-tag][data-tone="accent"] {
|
|
187
|
-
background:
|
|
188
|
-
linear-gradient(180deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 50%),
|
|
189
|
-
linear-gradient(180deg, #4FC3F7 0%, #0288D1 100%) !important;
|
|
190
|
-
color: #FFFFFF !important;
|
|
191
|
-
text-shadow: 0 1px 0 rgba(0, 50, 120, 0.4);
|
|
192
|
-
border-color: rgba(2, 119, 189, 0.7) !important;
|
|
193
|
-
}
|
|
194
|
-
html[data-ck-chrome="frutiger"] [data-ck-tag][data-tone="success"] {
|
|
195
|
-
background:
|
|
196
|
-
linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 50%),
|
|
197
|
-
linear-gradient(180deg, #DCFF66 0%, #C6FF00 100%) !important;
|
|
198
|
-
color: #2A3A00 !important;
|
|
199
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
|
200
|
-
border-color: rgba(120, 160, 0, 0.5) !important;
|
|
201
|
-
}
|
|
202
|
-
html[data-ck-chrome="frutiger"] [data-ck-tag][data-tone="warning"] {
|
|
203
|
-
background:
|
|
204
|
-
linear-gradient(180deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 50%),
|
|
205
|
-
linear-gradient(180deg, #FFCA28 0%, #FF9800 100%) !important;
|
|
206
|
-
color: #4A2400 !important;
|
|
207
|
-
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
|
|
208
|
-
border-color: rgba(180, 100, 0, 0.5) !important;
|
|
209
|
-
}
|
|
210
|
-
html[data-ck-chrome="frutiger"] [data-ck-tag][data-tone="critical"] {
|
|
211
|
-
background:
|
|
212
|
-
linear-gradient(180deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 50%),
|
|
213
|
-
linear-gradient(180deg, #FF7B7B 0%, #D32F2F 100%) !important;
|
|
214
|
-
color: #FFFFFF !important;
|
|
215
|
-
text-shadow: 0 1px 0 rgba(140, 20, 20, 0.6);
|
|
216
|
-
border-color: rgba(150, 30, 30, 0.7) !important;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/* ---------- Layout chrome ---------- */
|
|
220
|
-
html[data-ck-chrome="frutiger"] [data-ck-leftnav] {
|
|
221
|
-
background:
|
|
222
|
-
linear-gradient(180deg, rgba(255,255,255,0.7) 0%, rgba(255,255,255,0.45) 100%) !important;
|
|
223
|
-
backdrop-filter: blur(28px) saturate(150%);
|
|
224
|
-
-webkit-backdrop-filter: blur(28px) saturate(150%);
|
|
225
|
-
border-right: 1px solid rgba(255, 255, 255, 0.55);
|
|
226
|
-
box-shadow: inset -1px 0 0 rgba(0, 50, 120, 0.1),
|
|
227
|
-
4px 0 24px rgba(0, 100, 200, 0.12);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
html[data-ck-chrome="frutiger"] [data-ck-topbar] {
|
|
231
|
-
background:
|
|
232
|
-
linear-gradient(180deg, rgba(255,255,255,0.78) 0%, rgba(255,255,255,0.55) 100%) !important;
|
|
233
|
-
backdrop-filter: blur(28px) saturate(150%);
|
|
234
|
-
-webkit-backdrop-filter: blur(28px) saturate(150%);
|
|
235
|
-
border-bottom: 1px solid rgba(255, 255, 255, 0.5);
|
|
236
|
-
box-shadow:
|
|
237
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.85),
|
|
238
|
-
0 2px 12px rgba(0, 100, 200, 0.1);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/* NavItem — glassy pill, accent on active */
|
|
242
|
-
html[data-ck-chrome="frutiger"] [data-ck-navitem] {
|
|
243
|
-
background: transparent !important;
|
|
244
|
-
border-radius: 10px !important;
|
|
245
|
-
font-weight: 500 !important;
|
|
246
|
-
color: var(--ck-text-secondary) !important;
|
|
247
|
-
transition: background 200ms ease, box-shadow 200ms ease;
|
|
248
|
-
}
|
|
249
|
-
html[data-ck-chrome="frutiger"] [data-ck-navitem]:not([data-active="true"]):hover {
|
|
250
|
-
background:
|
|
251
|
-
linear-gradient(180deg, rgba(255,255,255,0.7) 0%, rgba(255,255,255,0.35) 100%) !important;
|
|
252
|
-
box-shadow:
|
|
253
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.7),
|
|
254
|
-
0 1px 4px rgba(0, 100, 200, 0.12) !important;
|
|
255
|
-
color: var(--ck-text-primary) !important;
|
|
256
|
-
}
|
|
257
|
-
html[data-ck-chrome="frutiger"] [data-ck-navitem][data-active="true"] {
|
|
258
|
-
background:
|
|
259
|
-
linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 50%),
|
|
260
|
-
linear-gradient(180deg, #4FC3F7 0%, #0288D1 100%) !important;
|
|
261
|
-
color: #FFFFFF !important;
|
|
262
|
-
text-shadow: 0 1px 0 rgba(0, 50, 120, 0.4) !important;
|
|
263
|
-
box-shadow:
|
|
264
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
|
265
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.18),
|
|
266
|
-
0 2px 8px rgba(0, 100, 200, 0.28) !important;
|
|
267
|
-
font-weight: 600 !important;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/* Search trigger */
|
|
271
|
-
html[data-ck-chrome="frutiger"] [data-ck-search-trigger] {
|
|
272
|
-
background:
|
|
273
|
-
linear-gradient(180deg, rgba(0, 50, 120, 0.06) 0%, rgba(255, 255, 255, 0.65) 18%) !important;
|
|
274
|
-
border: 1px solid rgba(0, 60, 140, 0.18) !important;
|
|
275
|
-
border-radius: 12px !important;
|
|
276
|
-
box-shadow:
|
|
277
|
-
inset 0 2px 4px rgba(0, 50, 120, 0.1),
|
|
278
|
-
0 1px 0 rgba(255, 255, 255, 0.85) !important;
|
|
279
|
-
color: var(--ck-text-secondary) !important;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/* Action bar */
|
|
283
|
-
html[data-ck-chrome="frutiger"] [data-ck-actionbar] {
|
|
284
|
-
background:
|
|
285
|
-
linear-gradient(180deg, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0.55) 100%) !important;
|
|
286
|
-
backdrop-filter: blur(28px) saturate(150%);
|
|
287
|
-
-webkit-backdrop-filter: blur(28px) saturate(150%);
|
|
288
|
-
border: 1px solid rgba(255, 255, 255, 0.6);
|
|
289
|
-
border-radius: 999px;
|
|
290
|
-
box-shadow:
|
|
291
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
|
292
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.14),
|
|
293
|
-
var(--ck-shadow-3);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/* Modal — frosted plate */
|
|
297
|
-
html[data-ck-chrome="frutiger"] [role="dialog"] > div:first-child {
|
|
298
|
-
background: rgba(135, 206, 235, 0.35);
|
|
299
|
-
backdrop-filter: blur(16px) saturate(160%);
|
|
300
|
-
-webkit-backdrop-filter: blur(16px) saturate(160%);
|
|
301
|
-
}
|
|
302
|
-
html[data-ck-chrome="frutiger"] [role="dialog"] > div > div {
|
|
303
|
-
background:
|
|
304
|
-
linear-gradient(180deg, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.65) 100%) !important;
|
|
305
|
-
backdrop-filter: blur(28px) saturate(160%);
|
|
306
|
-
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
|
307
|
-
border: 1px solid rgba(255, 255, 255, 0.7);
|
|
308
|
-
border-radius: 22px;
|
|
309
|
-
box-shadow:
|
|
310
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
|
311
|
-
inset 0 -1px 0 rgba(0, 50, 120, 0.14),
|
|
312
|
-
var(--ck-shadow-3);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/* Code / pre blocks — semi-transparent glass too */
|
|
316
|
-
html[data-ck-chrome="frutiger"] pre,
|
|
317
|
-
html[data-ck-chrome="frutiger"] code {
|
|
318
|
-
background: rgba(255, 255, 255, 0.55) !important;
|
|
319
|
-
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
320
|
-
border-radius: 10px;
|
|
321
|
-
color: var(--ck-text-primary);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/* ---------- Shimmer sweep — opt-in via .ck-frutiger-shimmer.
|
|
325
|
-
Diagonal white gradient sweeps across the element every few
|
|
326
|
-
seconds. Apply to "shiny" elements: progress fills, loading
|
|
327
|
-
pills, the hero CTA. */
|
|
328
|
-
@keyframes ck-frutiger-shimmer {
|
|
329
|
-
0% { transform: translateX(-150%) skewX(-20deg); }
|
|
330
|
-
60%, 100% { transform: translateX(250%) skewX(-20deg); }
|
|
331
|
-
}
|
|
332
|
-
.ck-frutiger-shimmer {
|
|
333
|
-
position: relative;
|
|
334
|
-
overflow: hidden;
|
|
335
|
-
}
|
|
336
|
-
.ck-frutiger-shimmer::after {
|
|
337
|
-
content: "";
|
|
338
|
-
position: absolute;
|
|
339
|
-
inset: 0;
|
|
340
|
-
pointer-events: none;
|
|
341
|
-
background: linear-gradient(110deg,
|
|
342
|
-
transparent 0%,
|
|
343
|
-
transparent 35%,
|
|
344
|
-
rgba(255, 255, 255, 0.55) 50%,
|
|
345
|
-
transparent 65%,
|
|
346
|
-
transparent 100%);
|
|
347
|
-
animation: ck-frutiger-shimmer 3.4s ease-in-out infinite;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/* Slow pulse for the "aero glow" — opt-in via .ck-frutiger-glow */
|
|
351
|
-
@keyframes ck-frutiger-glow-pulse {
|
|
352
|
-
0%, 100% { box-shadow: 0 0 16px rgba(79, 195, 247, 0.35); }
|
|
353
|
-
50% { box-shadow: 0 0 28px rgba(79, 195, 247, 0.65); }
|
|
354
|
-
}
|
|
355
|
-
.ck-frutiger-glow {
|
|
356
|
-
animation: ck-frutiger-glow-pulse 4.2s ease-in-out infinite;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
@media (prefers-reduced-motion: reduce) {
|
|
360
|
-
.ck-frutiger-shimmer::after,
|
|
361
|
-
.ck-frutiger-glow {
|
|
362
|
-
animation: none;
|
|
363
|
-
}
|
|
364
|
-
}
|