@godxjp/ui 28.7.0 → 28.8.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/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/marquee.d.ts +16 -0
- package/dist/components/data-display/marquee.js +155 -0
- package/dist/components/general/reveal.d.ts +23 -2
- package/dist/components/general/reveal.js +37 -7
- package/dist/components/general/typography.d.ts +4 -1
- package/dist/components/general/typography.js +14 -1
- package/dist/components/layout/affix.d.ts +86 -0
- package/dist/components/layout/affix.js +187 -0
- package/dist/components/layout/index.d.ts +4 -0
- package/dist/components/layout/index.js +4 -0
- package/dist/components/layout/legal-document-shell.js +4 -3
- package/dist/components/layout/masonry.d.ts +74 -0
- package/dist/components/layout/masonry.js +214 -0
- package/dist/components/layout/page-container.js +5 -20
- package/dist/components/navigation/anchor.d.ts +64 -0
- package/dist/components/navigation/anchor.js +284 -0
- package/dist/components/navigation/index.d.ts +4 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/navigation/mega-menu.d.ts +21 -0
- package/dist/components/navigation/mega-menu.js +526 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +517 -0
- package/dist/i18n/messages/ja.json +513 -0
- package/dist/i18n/messages/vi.json +513 -0
- package/dist/lib/hooks.d.ts +68 -0
- package/dist/lib/hooks.js +52 -0
- package/dist/lib/platform.d.ts +14 -0
- package/dist/lib/platform.js +10 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.js +3 -2
- package/dist/props/components/data-display.prop.d.ts +95 -1
- package/dist/props/components/general.prop.d.ts +47 -3
- package/dist/props/components/layout.prop.d.ts +194 -0
- package/dist/props/components/navigation.prop.d.ts +263 -0
- package/dist/props/registry.d.ts +359 -4
- package/dist/props/registry.js +472 -3
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +39 -2
- package/dist/styles/control.css +5 -6
- package/dist/styles/data-display-layout.css +2 -1
- package/dist/styles/density.css +4 -0
- package/dist/styles/layout.css +79 -0
- package/dist/styles/motion.css +121 -1
- package/dist/styles/navigation-layout.css +397 -1
- package/dist/styles/shell-layout.css +3 -0
- package/dist/styles/text-layout.css +52 -4
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/affix.css +7 -0
- package/dist/tokens/components/anchor.css +17 -0
- package/dist/tokens/components/control.css +3 -3
- package/dist/tokens/components/form.css +1 -1
- package/dist/tokens/components/marquee.css +7 -0
- package/dist/tokens/components/masonry.css +6 -0
- package/dist/tokens/components/mega-menu.css +62 -0
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/foundation.css +11 -0
- package/dist/tokens/semantic/layout.css +7 -0
- package/docs/COMPOSITION-VS-COMPONENT.md +19 -1
- package/docs/DESIGN-AUTHORITY.md +99 -18
- package/docs/FRAME-COVERAGE-REPORT.md +7 -2
- package/docs/data-display/marquee.tsx +254 -0
- package/docs/foundation/_theme-editor-scope.ts +222 -0
- package/docs/foundation/density.tsx +12 -2
- package/docs/foundation/spacing.tsx +5 -0
- package/docs/foundation/theme-editor.tsx +645 -0
- package/docs/general/activity.tsx +65 -0
- package/docs/general/reveal.tsx +290 -22
- package/docs/general/typography.tsx +91 -1
- package/docs/layout/affix.tsx +209 -0
- package/docs/layout/masonry.tsx +291 -0
- package/docs/navigation/anchor.tsx +285 -0
- package/docs/navigation/mega-menu-panel.tsx +86 -0
- package/docs/navigation/mega-menu.tsx +254 -0
- package/docs/roadmap/website-components.md +779 -0
- package/docs/showcase/acme-website.tsx +75 -39
- package/docs/showcase/futurelastic-web.tsx +91 -49
- package/docs/showcase/marketing-page.tsx +885 -0
- package/docs/showcase/table-footer-totals.tsx +12 -2
- package/docs/showcase/theme-customization.tsx +1259 -0
- package/package.json +5 -3
- package/scripts/brand-accent.generated.mjs +27 -0
- package/scripts/ui-audit.mjs +66 -0
- package/scripts/visual-audit-rules.mjs +46 -2
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
Warehouse,
|
|
33
33
|
} from "lucide-react";
|
|
34
34
|
|
|
35
|
-
import { Button, Text } from "@godxjp/ui/general";
|
|
35
|
+
import { Button, Heading, Icon, Text } from "@godxjp/ui/general";
|
|
36
36
|
import { Avatar, AvatarFallback, Card, CardContent, CardCover } from "@godxjp/ui/data-display";
|
|
37
37
|
import { Flex, ResponsiveGrid } from "@godxjp/ui/layout";
|
|
38
38
|
|
|
@@ -52,6 +52,7 @@ const THEME = `
|
|
|
52
52
|
3.18 on --muted. It was 216 16% 80% = 1.46:1. --border is decorative chrome; it stays. */
|
|
53
53
|
--border: 214 27% 90%; --input: 216 16% 55%;
|
|
54
54
|
--success: 152 100% 25%; --warning: 41 73% 53%; --destructive: 11 82% 56%; --info: 203 100% 37%;
|
|
55
|
+
--letter-spacing-tight: -0.02em; /* the display tracking .tx-display/.tx-h2 hand-wrote, once */
|
|
55
56
|
--radius: 0.875rem; --radius-md: 10px; --radius-lg: 14px;
|
|
56
57
|
--card-radius: var(--radius); --control-radius: var(--radius);
|
|
57
58
|
--shadow-color: 38 79 145;
|
|
@@ -62,33 +63,44 @@ const THEME = `
|
|
|
62
63
|
--font-family-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
63
64
|
--font-family-sans: var(--font-family-body);
|
|
64
65
|
--text-brand: 41 84% 26%; /* dark gold, eyebrows clear AA on light sections */
|
|
66
|
+
/* The glass bar, opted into once (gh#831). These two used to be a literal 0.85 alpha and a
|
|
67
|
+
literal 10px blur on .tx-navbar. futurelastic-web.tsx hand-wrote the identical pair. */
|
|
68
|
+
--topbar-background-alpha: 85%;
|
|
69
|
+
--topbar-backdrop-blur-size: 10px;
|
|
65
70
|
}
|
|
66
71
|
/* Marketing layout + type (a consumer's section stylesheet) */
|
|
67
|
-
[data-tenant="acme-web"] .tx-shell { margin-inline: auto; width: 100%; max-
|
|
72
|
+
[data-tenant="acme-web"] .tx-shell { margin-inline: auto; width: 100%; max-inline-size: var(--page-measure-wide);
|
|
73
|
+
padding-inline: var(--space-8);
|
|
68
74
|
/* ResponsiveGrid uses @container queries, so establish the container here (PageContainer does this
|
|
69
75
|
* in an app; a marketing composition provides it on its own shell). */
|
|
70
76
|
container-type: inline-size; }
|
|
71
|
-
[data-tenant="acme-web"] .tx-section { padding-block:
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
[data-tenant="acme-web"] .tx-section { padding-block: var(--space-section-band); }
|
|
78
|
+
/* The sticky header IS this page's bar region, so it reads the --topbar-* glass knobs. It is not a
|
|
79
|
+
<Topbar> for one reason: the glass must span the full bleed while the bar's CONTENT stays on the
|
|
80
|
+
.tx-shell measure, and Topbar is a single flex row with no inner shell. */
|
|
81
|
+
[data-tenant="acme-web"] .tx-navbar { position: sticky; inset-block-start: 0; z-index: 30;
|
|
82
|
+
background-color: hsl(var(--background) / var(--topbar-background-alpha));
|
|
83
|
+
backdrop-filter: blur(var(--topbar-backdrop-blur-size));
|
|
84
|
+
border-block-end: var(--stroke-hairline) solid hsl(var(--border)); }
|
|
74
85
|
[data-tenant="acme-web"] .tx-navbar-inner { height: 72px; display: flex; align-items: center; gap: 2.25rem; }
|
|
75
86
|
[data-tenant="acme-web"] .tx-brand { font-family: var(--font-family-display); font-weight: 800; font-size: 1.375rem; letter-spacing: -0.01em; color: hsl(var(--foreground)); }
|
|
76
87
|
[data-tenant="acme-web"] .tx-eyebrow { font-family: var(--font-family-display); font-weight: 700; font-size: 0.8125rem;
|
|
77
88
|
letter-spacing: 0.12em; text-transform: uppercase; color: hsl(var(--text-brand)); }
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
/* .tx-display / .tx-h2 / .tx-lead are GONE (gh#826). They existed only because the display type
|
|
90
|
+
ramp had tokens and no prop, so all three were a raw font-size plus the leading that has to come
|
|
91
|
+
with it. They are now Heading size="5xl", Heading size="4xl" and Text size="xl". The
|
|
92
|
+
brand face is not lost with them: base.css already points every <h1>-<h6> at
|
|
93
|
+
--font-family-display, which the token block above sets. The one knob those classes carried that
|
|
94
|
+
is genuinely brand and not scale is the display tracking, and it IS a token — set once, below. */
|
|
83
95
|
/* --text-brand, not --primary (gh#643): --primary is a FILL role, and as ink it measured
|
|
84
96
|
2.01:1 here. The AA-safe text tier already exists and .tx-eyebrow above already reads it. */
|
|
85
97
|
[data-tenant="acme-web"] .tx-gold { color: hsl(var(--text-brand)); }
|
|
86
98
|
[data-tenant="acme-web"] .tx-stat { font-family: var(--font-family-display); font-weight: 800; font-size: 1.75rem; color: hsl(var(--primary)); }
|
|
87
|
-
[data-tenant="acme-web"] .tx-hero-grid { display: grid; gap:
|
|
99
|
+
[data-tenant="acme-web"] .tx-hero-grid { display: grid; gap: var(--space-12); align-items: center; padding-block: var(--space-section-hero); }
|
|
88
100
|
@media (min-width: 1024px) { [data-tenant="acme-web"] .tx-hero-grid { grid-template-columns: 1.1fr 0.9fr; } }
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
/* .tx-glow* are GONE too: .ui-brand-glow shipped with exactly this gradient and these four
|
|
102
|
+
knobs, and nothing in the repo referenced it while four sections hand-wrote it (gh#826). The
|
|
103
|
+
halo's position/size/alpha are now per-instance token overrides at the call site. */
|
|
92
104
|
[data-tenant="acme-web"] .tx-field { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem;
|
|
93
105
|
background: hsl(0 0% 100% / 0.08); border-radius: var(--radius-md); }
|
|
94
106
|
[data-tenant="acme-web"] .tx-quote-total { display: flex; align-items: center; justify-content: space-between;
|
|
@@ -98,10 +110,7 @@ const THEME = `
|
|
|
98
110
|
[data-tenant="acme-web"] .tx-footer-grid { display: grid; gap: 2.25rem; }
|
|
99
111
|
@media (min-width: 768px) { [data-tenant="acme-web"] .tx-footer-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
|
|
100
112
|
[data-tenant="acme-web"] .tx-footer-bottom { display: flex; align-items: center; justify-content: space-between;
|
|
101
|
-
margin-
|
|
102
|
-
[data-tenant="acme-web"] .tx-medallion svg { width: 1.25rem; height: 1.25rem; }
|
|
103
|
-
[data-tenant="acme-web"] .tx-icon-18 { width: 1.125rem; height: 1.125rem; }
|
|
104
|
-
[data-tenant="acme-web"] .tx-icon-22 { width: 1.375rem; height: 1.375rem; }
|
|
113
|
+
margin-block-start: 2.25rem; padding-block-start: var(--space-5); border-block-start: var(--stroke-hairline) solid hsl(0 0% 100% / 0.1); }
|
|
105
114
|
/* Navy region: role scoping so descendants render on-dark (white text, glass cards, outline btns). */
|
|
106
115
|
[data-tenant="acme-web"] .tx-navy {
|
|
107
116
|
background: hsl(217 61% 12%); --foreground: 0 0% 100%;
|
|
@@ -156,19 +165,28 @@ function Hero() {
|
|
|
156
165
|
];
|
|
157
166
|
return (
|
|
158
167
|
<section className="tx-navy relative overflow-hidden">
|
|
159
|
-
<div
|
|
168
|
+
<div
|
|
169
|
+
className="ui-brand-glow absolute inset-0"
|
|
170
|
+
style={
|
|
171
|
+
{
|
|
172
|
+
"--brand-glow-size": "40% 50%",
|
|
173
|
+
"--brand-glow-position": "88% -10%",
|
|
174
|
+
"--brand-glow-alpha": "0.22",
|
|
175
|
+
} as React.CSSProperties
|
|
176
|
+
}
|
|
177
|
+
/>
|
|
160
178
|
<div className={`${SHELL} tx-hero-grid relative`}>
|
|
161
179
|
<Flex direction="col" gap="xl">
|
|
162
180
|
<div className="tx-eyebrow">Mua hộ & vận chuyển quốc tế</div>
|
|
163
|
-
<
|
|
181
|
+
<Heading level={1} size="5xl" weight="bold">
|
|
164
182
|
Mua sắm toàn cầu,
|
|
165
183
|
<br />
|
|
166
184
|
<span className="tx-gold">ACME</span> lo phần còn lại.
|
|
167
|
-
</
|
|
168
|
-
<p
|
|
185
|
+
</Heading>
|
|
186
|
+
<Text as="p" size="xl" tone="muted">
|
|
169
187
|
Đặt mua, thanh toán và vận chuyển hàng từ Nhật, Hàn, Indonesia và Mỹ về tận nhà. Minh
|
|
170
188
|
bạch chi phí, theo dõi từng bước.
|
|
171
|
-
</
|
|
189
|
+
</Text>
|
|
172
190
|
<Flex direction="row" gap="md">
|
|
173
191
|
<Button size="lg">
|
|
174
192
|
Nhận báo giá miễn phí
|
|
@@ -196,9 +214,9 @@ function Hero() {
|
|
|
196
214
|
<Flex direction="col" gap="md">
|
|
197
215
|
<div className="tx-eyebrow">Ước tính phí vận chuyển</div>
|
|
198
216
|
<Flex direction="col" gap="sm">
|
|
199
|
-
{fields.map(([
|
|
217
|
+
{fields.map(([Glyph, label, value]) => (
|
|
200
218
|
<div key={label} className="tx-field">
|
|
201
|
-
<Icon
|
|
219
|
+
<Icon as={Glyph} size="lg" className="tx-gold" />
|
|
202
220
|
<div className="min-w-0 flex-1">
|
|
203
221
|
<Text as="div" size="2xs" tone="muted">
|
|
204
222
|
{label}
|
|
@@ -207,7 +225,7 @@ function Hero() {
|
|
|
207
225
|
{value}
|
|
208
226
|
</Text>
|
|
209
227
|
</div>
|
|
210
|
-
<
|
|
228
|
+
<Icon as={ChevronDown} size="lg" />
|
|
211
229
|
</div>
|
|
212
230
|
))}
|
|
213
231
|
<div className="tx-quote-total">
|
|
@@ -241,17 +259,23 @@ function SectionHead({
|
|
|
241
259
|
return (
|
|
242
260
|
<div className={center ? "mx-auto text-center" : undefined}>
|
|
243
261
|
<div className="tx-eyebrow">{eyebrow}</div>
|
|
244
|
-
<
|
|
245
|
-
|
|
262
|
+
<Heading level={2} size="4xl" weight="bold">
|
|
263
|
+
{title}
|
|
264
|
+
</Heading>
|
|
265
|
+
{sub ? (
|
|
266
|
+
<Text as="p" size="xl" tone="muted" className="mx-auto">
|
|
267
|
+
{sub}
|
|
268
|
+
</Text>
|
|
269
|
+
) : null}
|
|
246
270
|
</div>
|
|
247
271
|
);
|
|
248
272
|
}
|
|
249
273
|
|
|
250
|
-
function Medallion({ icon:
|
|
274
|
+
function Medallion({ icon: Glyph }: { icon: typeof Globe }) {
|
|
251
275
|
return (
|
|
252
276
|
<Avatar className="rounded-md">
|
|
253
|
-
<AvatarFallback className="bg-primary/10 text-primary
|
|
254
|
-
<Icon
|
|
277
|
+
<AvatarFallback className="bg-primary/10 text-primary rounded-md">
|
|
278
|
+
<Icon as={Glyph} size="lg" />
|
|
255
279
|
</AvatarFallback>
|
|
256
280
|
</Avatar>
|
|
257
281
|
);
|
|
@@ -315,7 +339,7 @@ function Steps() {
|
|
|
315
339
|
<Flex direction="col" gap="xl" className={SHELL}>
|
|
316
340
|
<SectionHead eyebrow="Quy trình" title="Mua hàng quốc tế trong 4 bước" />
|
|
317
341
|
<ResponsiveGrid columns={{ sm: 1, md: 2, lg: 4 }}>
|
|
318
|
-
{steps.map(([
|
|
342
|
+
{steps.map(([Glyph, t, d], i) => (
|
|
319
343
|
<Flex direction="col" gap="md" key={t}>
|
|
320
344
|
<Flex direction="row" gap="sm" align="center">
|
|
321
345
|
<Avatar>
|
|
@@ -323,7 +347,7 @@ function Steps() {
|
|
|
323
347
|
{i + 1}
|
|
324
348
|
</AvatarFallback>
|
|
325
349
|
</Avatar>
|
|
326
|
-
<Icon
|
|
350
|
+
<Icon as={Glyph} size="xl" className="text-brand" />
|
|
327
351
|
</Flex>
|
|
328
352
|
<Text as="div" weight="bold" size="lg">
|
|
329
353
|
{t}
|
|
@@ -380,10 +404,22 @@ function Routes() {
|
|
|
380
404
|
function CtaBanner() {
|
|
381
405
|
return (
|
|
382
406
|
<section className="tx-navy tx-section relative overflow-hidden">
|
|
383
|
-
<div
|
|
407
|
+
<div
|
|
408
|
+
className="ui-brand-glow absolute inset-0"
|
|
409
|
+
style={
|
|
410
|
+
{
|
|
411
|
+
"--brand-glow-size": "40% 50%",
|
|
412
|
+
"--brand-glow-position": "12% 120%",
|
|
413
|
+
} as React.CSSProperties
|
|
414
|
+
}
|
|
415
|
+
/>
|
|
384
416
|
<div className={`${SHELL} relative text-center`}>
|
|
385
|
-
<
|
|
386
|
-
|
|
417
|
+
<Heading level={2} size="4xl" weight="bold">
|
|
418
|
+
Sẵn sàng cho đơn hàng đầu tiên?
|
|
419
|
+
</Heading>
|
|
420
|
+
<Text as="p" size="xl" tone="muted" className="mx-auto">
|
|
421
|
+
Tạo tài khoản miễn phí và nhận báo giá trong 5 phút.
|
|
422
|
+
</Text>
|
|
387
423
|
<Flex direction="row" gap="md" justify="center">
|
|
388
424
|
<Button size="lg">
|
|
389
425
|
<UserPlus aria-hidden="true" />
|
|
@@ -406,7 +442,7 @@ function Footer() {
|
|
|
406
442
|
];
|
|
407
443
|
return (
|
|
408
444
|
<footer className="tx-navy tx-navy-deep">
|
|
409
|
-
<div className={`${SHELL} tx-footer-grid`} style={{ paddingBlock: "3.5rem
|
|
445
|
+
<div className={`${SHELL} tx-footer-grid`} style={{ paddingBlock: "3.5rem var(--space-8)" }}>
|
|
410
446
|
<Flex direction="col" gap="md">
|
|
411
447
|
<span className="tx-brand">
|
|
412
448
|
TIXI<span className="tx-gold">MAX</span>
|
|
@@ -438,8 +474,8 @@ function Footer() {
|
|
|
438
474
|
2026 ACME Logistics. All rights reserved.
|
|
439
475
|
</Text>
|
|
440
476
|
<Flex direction="row" gap="md">
|
|
441
|
-
{[Mail, Phone, Globe].map((
|
|
442
|
-
<Icon key={i}
|
|
477
|
+
{[Mail, Phone, Globe].map((Glyph, i) => (
|
|
478
|
+
<Icon key={i} as={Glyph} size="lg" tone="muted" />
|
|
443
479
|
))}
|
|
444
480
|
</Flex>
|
|
445
481
|
</div>
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
AtSign,
|
|
24
24
|
} from "lucide-react";
|
|
25
25
|
|
|
26
|
-
import { Button, Text } from "@godxjp/ui/general";
|
|
26
|
+
import { Button, Heading, Icon, Text } from "@godxjp/ui/general";
|
|
27
27
|
import { Avatar, AvatarFallback, Card, CardContent } from "@godxjp/ui/data-display";
|
|
28
28
|
import { Flex } from "@godxjp/ui/layout";
|
|
29
29
|
|
|
@@ -46,6 +46,7 @@ const THEME = `
|
|
|
46
46
|
a card, 3.21 on --muted. It was 36 18% 22% = 1.52:1. --border is decorative chrome; it stays. */
|
|
47
47
|
--border: 36 22% 14%; --input: 36 18% 40%;
|
|
48
48
|
--success: 145 63% 49%; --warning: 33 90% 44%; --destructive: 0 84% 60%; --info: 213 94% 68%;
|
|
49
|
+
--letter-spacing-tight: -0.03em; /* the display tracking .fl-display/.fl-h2 hand-wrote, once */
|
|
49
50
|
--radius: 1rem; --radius-md: 10px; --radius-lg: 14px; --radius-xl: 20px; --radius-2xl: 24px;
|
|
50
51
|
--card-radius: var(--radius-2xl); --control-radius: var(--radius-lg);
|
|
51
52
|
--shadow-color: 0 0 0;
|
|
@@ -54,15 +55,32 @@ const THEME = `
|
|
|
54
55
|
--font-family-display: "Sora", system-ui, sans-serif;
|
|
55
56
|
--font-family-body: "Be Vietnam Pro", system-ui, "Noto Sans JP", sans-serif;
|
|
56
57
|
--font-family-sans: var(--font-family-body);
|
|
57
|
-
--font-size-display
|
|
58
|
+
/* 80px hero. --font-size-display is the display ramp's BASE KNOB, but overriding it HERE does
|
|
59
|
+
not move the derived steps: --font-size-5xl is declared at :root as var(--font-size-display),
|
|
60
|
+
so it computes on :root and merely INHERITS down — the freeze docs/TOKENS.md names, and the
|
|
61
|
+
reason its role-mirror knobs are declared initial. The whole --font-size-* scale is built this way
|
|
62
|
+
(--font-size-lg freezes on --font-size-base identically), so it is a systemic token-tier
|
|
63
|
+
defect, not this ramp's, and not gh#826's to fix. Measured, not assumed: with only the line
|
|
64
|
+
below, <Heading size="5xl"> painted 54px here, the :root value. Setting the STEP works,
|
|
65
|
+
because this declaration is on an ancestor of the heading. */
|
|
66
|
+
--font-size-display: 5rem;
|
|
67
|
+
--font-size-5xl: 5rem;
|
|
68
|
+
/* The glass bar, opted into once (gh#831). acme-website.tsx hand-wrote the identical pair,
|
|
69
|
+
which is why these are framework knobs now and not two literals on .fl-navbar. */
|
|
70
|
+
--topbar-background-alpha: 80%;
|
|
71
|
+
--topbar-backdrop-blur-size: 12px;
|
|
58
72
|
color-scheme: dark;
|
|
59
73
|
}
|
|
60
74
|
/* Marketing surface (consumer section stylesheet) */
|
|
61
75
|
[data-tenant="futurelastic"] { background: hsl(var(--background)); color: hsl(var(--foreground)); min-height: 100vh; }
|
|
62
|
-
[data-tenant="futurelastic"] .fl-shell { margin-inline: auto; width: 100%; max-
|
|
63
|
-
[data-tenant="futurelastic"] .fl-section { padding-block:
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
[data-tenant="futurelastic"] .fl-shell { margin-inline: auto; width: 100%; max-inline-size: var(--page-measure-wide); padding-inline: var(--space-8); container-type: inline-size; }
|
|
77
|
+
[data-tenant="futurelastic"] .fl-section { padding-block: var(--space-section-band); }
|
|
78
|
+
/* The sticky header IS this page's bar region, so it reads the --topbar-* glass knobs. See the
|
|
79
|
+
matching note in acme-website.tsx for why it is not a <Topbar>. */
|
|
80
|
+
[data-tenant="futurelastic"] .fl-navbar { position: sticky; inset-block-start: 0; z-index: 30;
|
|
81
|
+
background-color: hsl(var(--background) / var(--topbar-background-alpha));
|
|
82
|
+
backdrop-filter: blur(var(--topbar-backdrop-blur-size));
|
|
83
|
+
border-block-end: var(--stroke-hairline) solid hsl(var(--border)); }
|
|
66
84
|
/* REFLOW, NOT DECORATION (gh#643). This row was a fixed 68px flex line with no wrap, so at 320 its
|
|
67
85
|
content ran to x=569.6 inside a 320px nav: every link painted OUTSIDE the navbar's own
|
|
68
86
|
translucent box, over the page behind it, and axe read the white labels against the light
|
|
@@ -71,53 +89,57 @@ const THEME = `
|
|
|
71
89
|
element looks like from the outside. min-height rather than height, so a wrapped row may be
|
|
72
90
|
taller than one line and the language cluster stops overflowing the bar vertically (measured at
|
|
73
91
|
320: it was 148.3px tall, starting at y=-40.2). */
|
|
74
|
-
[data-tenant="futurelastic"] .fl-navbar-inner { min-
|
|
92
|
+
[data-tenant="futurelastic"] .fl-navbar-inner { min-block-size: 68px; display: flex; align-items: center; flex-wrap: wrap; column-gap: var(--space-8); row-gap: var(--space-3); padding-block: var(--space-3); }
|
|
75
93
|
[data-tenant="futurelastic"] .fl-brand { font-family: var(--font-family-display); font-weight: 700; font-size: 1.25rem; color: hsl(var(--foreground)); }
|
|
76
94
|
[data-tenant="futurelastic"] .fl-eyebrow { font-family: var(--font-family-body); font-weight: 600; font-size: 0.75rem;
|
|
77
95
|
letter-spacing: 0.18em; text-transform: uppercase; color: hsl(var(--primary)); }
|
|
78
|
-
[data-tenant="futurelastic"] .fl-badge { display: inline-flex; align-items: center; gap:
|
|
79
|
-
border:
|
|
96
|
+
[data-tenant="futurelastic"] .fl-badge { display: inline-flex; align-items: center; gap: var(--space-2); padding: 0.375rem 0.875rem;
|
|
97
|
+
border: var(--stroke-hairline) solid hsl(var(--primary) / 0.3); background: hsl(var(--primary) / 0.08); border-radius: var(--radius-pill);
|
|
80
98
|
font-size: 0.8125rem; font-weight: 500; color: hsl(var(--foreground)); }
|
|
81
|
-
[data-tenant="futurelastic"] .fl-badge .dot {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
99
|
+
[data-tenant="futurelastic"] .fl-badge .dot { inline-size: 6px; block-size: 6px; border-radius: var(--radius-pill); background: hsl(var(--primary)); }
|
|
100
|
+
/* The hero band still breathes with the viewport, but both ends of the clamp are now the band/hero
|
|
101
|
+
steps rather than an eyeballed 5rem…10rem (gh#831). */
|
|
102
|
+
[data-tenant="futurelastic"] .fl-hero { position: relative; overflow: hidden; padding-block: clamp(var(--space-section-band), 3rem + 9vw, var(--space-section-hero)); }
|
|
103
|
+
/* The halo is .ui-brand-glow (src/styles/layout.css), which ships and was going unused. This
|
|
104
|
+
class and .fl-cta-glow now only place it and set its documented knobs. */
|
|
105
|
+
[data-tenant="futurelastic"] .fl-hero-glow { position: absolute; inset: 0;
|
|
106
|
+
--brand-glow-color: 42 54% 54%; --brand-glow-size: 60% 50%; --brand-glow-position: 70% 0%; --brand-glow-alpha: 0.12; }
|
|
85
107
|
[data-tenant="futurelastic"] .fl-hero-inner { position: relative; max-width: 860px; }
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
/* .fl-display / .fl-h2 / .fl-lead / .fl-hero-glow / .fl-cta-glow are GONE (gh#826) — and the fact
|
|
109
|
+
that this file and acme-website.tsx, two unrelated brands, had independently written the SAME
|
|
110
|
+
five is what made them a missing API rather than brand styling. They are now
|
|
111
|
+
Heading size="5xl", Heading size="4xl", Text size="xl" and two .ui-brand-glow
|
|
112
|
+
layers. The display face still arrives through --font-family-display (base.css points every
|
|
113
|
+
<h1>-<h6> at it); the tracking is a token, set once above. */
|
|
90
114
|
[data-tenant="futurelastic"] .fl-gold { color: hsl(var(--primary)); }
|
|
91
|
-
[data-tenant="futurelastic"] .fl-
|
|
92
|
-
[data-tenant="futurelastic"] .fl-note { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); margin-top: 2.5rem; }
|
|
115
|
+
[data-tenant="futurelastic"] .fl-note { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); margin-block-start: var(--space-10); }
|
|
93
116
|
[data-tenant="futurelastic"] .fl-head { max-width: 640px; margin-inline: auto; text-align: center; }
|
|
94
|
-
[data-tenant="futurelastic"] .fl-hero-actions { margin-
|
|
117
|
+
[data-tenant="futurelastic"] .fl-hero-actions { margin-block-start: 1.75rem; }
|
|
95
118
|
[data-tenant="futurelastic"] .fl-cta-inner { position: relative; max-width: 680px; margin-inline: auto; }
|
|
96
119
|
[data-tenant="futurelastic"] .fl-footer-tagline { max-width: 260px; }
|
|
97
|
-
[data-tenant="futurelastic"] .fl-logocloud { display: flex; flex-wrap: wrap; justify-content: center; gap:
|
|
98
|
-
[data-tenant="futurelastic"] .fl-logo-item { display: inline-flex; align-items: center; gap:
|
|
120
|
+
[data-tenant="futurelastic"] .fl-logocloud { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-4) var(--space-10); }
|
|
121
|
+
[data-tenant="futurelastic"] .fl-logo-item { display: inline-flex; align-items: center; gap: var(--space-2); font-family: var(--font-family-display);
|
|
99
122
|
font-weight: 600; font-size: 1.0625rem; color: hsl(var(--foreground) / 0.85); }
|
|
100
123
|
[data-tenant="futurelastic"] .fl-logo-item .tag { font-family: var(--font-family-body); font-weight: 500; font-size: 0.6875rem;
|
|
101
124
|
letter-spacing: 0.06em; text-transform: uppercase; color: hsl(var(--primary)); }
|
|
102
|
-
[data-tenant="futurelastic"] .fl-section-tint { background: hsl(var(--card) / 0.5); border-block:
|
|
103
|
-
[data-tenant="futurelastic"] .fl-bento { display: grid; grid-template-columns: 1fr; gap:
|
|
125
|
+
[data-tenant="futurelastic"] .fl-section-tint { background: hsl(var(--card) / 0.5); border-block: var(--stroke-hairline) solid hsl(var(--border)); padding-block: var(--space-12); }
|
|
126
|
+
[data-tenant="futurelastic"] .fl-bento { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
|
|
104
127
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento { grid-template-columns: repeat(6, 1fr); } }
|
|
105
128
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento > .fl-cell { grid-column: span 2; } }
|
|
106
129
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento > .fl-cell.wide { grid-column: span 4; } }
|
|
107
130
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento > .fl-cell.half { grid-column: span 3; } }
|
|
108
|
-
[data-tenant="futurelastic"] .fl-
|
|
109
|
-
[data-tenant="futurelastic"] .fl-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem; }
|
|
131
|
+
[data-tenant="futurelastic"] .fl-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-8); }
|
|
110
132
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-stats { grid-template-columns: repeat(4, 1fr); } }
|
|
111
133
|
[data-tenant="futurelastic"] .fl-stat-num { font-family: var(--font-family-display); font-weight: 700; font-size: 3rem; color: hsl(var(--primary)); line-height: 1; }
|
|
112
|
-
[data-tenant="futurelastic"] .fl-cta { position: relative; overflow: hidden; text-align: center; border:
|
|
113
|
-
border-radius: var(--radius-2xl); background: hsl(var(--card)); padding: 4rem
|
|
114
|
-
[data-tenant="futurelastic"] .fl-cta-glow { position: absolute; inset: 0;
|
|
115
|
-
|
|
116
|
-
[data-tenant="futurelastic"] .fl-footer { border-
|
|
117
|
-
[data-tenant="futurelastic"] .fl-footer-grid { display: grid; grid-template-columns: 1fr; gap:
|
|
134
|
+
[data-tenant="futurelastic"] .fl-cta { position: relative; overflow: hidden; text-align: center; border: var(--stroke-hairline) solid hsl(var(--primary) / 0.2);
|
|
135
|
+
border-radius: var(--radius-2xl); background: hsl(var(--card)); padding: 4rem var(--space-8); }
|
|
136
|
+
[data-tenant="futurelastic"] .fl-cta-glow { position: absolute; inset: 0;
|
|
137
|
+
--brand-glow-color: 42 54% 54%; --brand-glow-size: 60% 80%; --brand-glow-position: 50% 0%; --brand-glow-alpha: 0.14; }
|
|
138
|
+
[data-tenant="futurelastic"] .fl-footer { border-block-start: var(--stroke-hairline) solid hsl(var(--border)); padding-block: 3.5rem var(--space-8); }
|
|
139
|
+
[data-tenant="futurelastic"] .fl-footer-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-10); }
|
|
118
140
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-footer-grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; } }
|
|
119
|
-
[data-tenant="futurelastic"] .fl-footer-bottom { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap:
|
|
120
|
-
margin-
|
|
141
|
+
[data-tenant="futurelastic"] .fl-footer-bottom { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-4);
|
|
142
|
+
margin-block-start: var(--space-10); padding-block-start: var(--space-6); border-block-start: var(--stroke-hairline) solid hsl(var(--border)); }
|
|
121
143
|
`;
|
|
122
144
|
|
|
123
145
|
const SHELL = "fl-shell";
|
|
@@ -151,18 +173,26 @@ function Navbar() {
|
|
|
151
173
|
function Hero() {
|
|
152
174
|
return (
|
|
153
175
|
<header className="fl-hero">
|
|
154
|
-
<div
|
|
176
|
+
<div
|
|
177
|
+
className="ui-brand-glow absolute inset-0"
|
|
178
|
+
style={
|
|
179
|
+
{
|
|
180
|
+
"--brand-glow-alpha": "0.12",
|
|
181
|
+
"--brand-glow-position": "70% 0%",
|
|
182
|
+
} as React.CSSProperties
|
|
183
|
+
}
|
|
184
|
+
/>
|
|
155
185
|
<div className={`${SHELL} fl-hero-inner`}>
|
|
156
186
|
<span className="fl-badge">
|
|
157
187
|
<span className="dot" /> Tech · AI · Holdings
|
|
158
188
|
</span>
|
|
159
|
-
<
|
|
189
|
+
<Heading level={1} size="5xl" weight="bold">
|
|
160
190
|
Building the <span className="fl-gold">elastic</span> infrastructure of the future.
|
|
161
|
-
</
|
|
162
|
-
<p
|
|
191
|
+
</Heading>
|
|
192
|
+
<Text as="p" size="xl" tone="muted">
|
|
163
193
|
Futurelastic là pháp nhân holding của nhóm công ty công nghệ & AI vận hành xuyên Nhật
|
|
164
194
|
Bản và Việt Nam, từ logistics, fintech đến F&B và thương mại.
|
|
165
|
-
</
|
|
195
|
+
</Text>
|
|
166
196
|
<Flex direction="row" gap="md" className="fl-hero-actions">
|
|
167
197
|
<Button size="lg">Khám phá hệ sinh thái</Button>
|
|
168
198
|
<Button variant="outline" size="lg">
|
|
@@ -201,11 +231,11 @@ function LogoCloud() {
|
|
|
201
231
|
);
|
|
202
232
|
}
|
|
203
233
|
|
|
204
|
-
function Medallion({ icon:
|
|
234
|
+
function Medallion({ icon: Glyph }: { icon: typeof Boxes }) {
|
|
205
235
|
return (
|
|
206
236
|
<Avatar className="rounded-lg">
|
|
207
|
-
<AvatarFallback className="bg-primary/10 text-primary
|
|
208
|
-
<Icon
|
|
237
|
+
<AvatarFallback className="bg-primary/10 text-primary rounded-lg">
|
|
238
|
+
<Icon as={Glyph} size="xl" />
|
|
209
239
|
</AvatarFallback>
|
|
210
240
|
</Avatar>
|
|
211
241
|
);
|
|
@@ -247,10 +277,12 @@ function Bento() {
|
|
|
247
277
|
<Flex direction="col" gap="xl" className={SHELL}>
|
|
248
278
|
<div className="fl-head">
|
|
249
279
|
<div className="fl-eyebrow">Hệ sinh thái</div>
|
|
250
|
-
<
|
|
251
|
-
|
|
280
|
+
<Heading level={2} size="4xl" weight="bold">
|
|
281
|
+
Một mái nhà, nhiều mũi nhọn
|
|
282
|
+
</Heading>
|
|
283
|
+
<Text as="p" size="xl" tone="muted" className="mx-auto">
|
|
252
284
|
Mỗi entity tự chủ vận hành; Futurelastic cung cấp vốn, công nghệ nền và quản trị chung.
|
|
253
|
-
</
|
|
285
|
+
</Text>
|
|
254
286
|
</div>
|
|
255
287
|
<div className="fl-bento">
|
|
256
288
|
{cells.map((c) => (
|
|
@@ -304,13 +336,23 @@ function Cta() {
|
|
|
304
336
|
<section className="fl-section">
|
|
305
337
|
<div className={SHELL}>
|
|
306
338
|
<div className="fl-cta">
|
|
307
|
-
<div
|
|
339
|
+
<div
|
|
340
|
+
className="ui-brand-glow absolute inset-0"
|
|
341
|
+
style={
|
|
342
|
+
{
|
|
343
|
+
"--brand-glow-alpha": "0.14",
|
|
344
|
+
"--brand-glow-size": "60% 80%",
|
|
345
|
+
} as React.CSSProperties
|
|
346
|
+
}
|
|
347
|
+
/>
|
|
308
348
|
<Flex direction="col" gap="xl" className="fl-cta-inner">
|
|
309
|
-
<
|
|
310
|
-
|
|
349
|
+
<Heading level={2} size="4xl" weight="bold">
|
|
350
|
+
Cùng xây điều mới?
|
|
351
|
+
</Heading>
|
|
352
|
+
<Text as="p" size="xl" tone="muted" className="mx-auto">
|
|
311
353
|
Chúng tôi tìm đối tác, nhân tài và cơ hội M&A trong công nghệ, logistics và
|
|
312
354
|
F&B tại Nhật Bản & Việt Nam.
|
|
313
|
-
</
|
|
355
|
+
</Text>
|
|
314
356
|
<Flex direction="row" gap="md" className="justify-center">
|
|
315
357
|
<Button size="lg">Đặt lịch trao đổi</Button>
|
|
316
358
|
<Button variant="ghost" size="lg">
|