@godxjp/ui 28.7.0 → 28.9.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/lib/variants.js +4 -1
- 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/props/vocabulary/layout.prop.d.ts +1 -1
- package/dist/styles/base.css +47 -14
- package/dist/styles/card-layout.css +2 -2
- package/dist/styles/chart-layout.css +6 -6
- package/dist/styles/control.css +15 -10
- package/dist/styles/data-display-layout.css +22 -6
- package/dist/styles/density.css +6 -0
- package/dist/styles/dialog-layout.css +4 -1
- package/dist/styles/focus-ring.css +4 -1
- package/dist/styles/layout.css +92 -3
- package/dist/styles/motion.css +121 -1
- package/dist/styles/navigation-layout.css +397 -1
- package/dist/styles/shell-layout.css +28 -21
- package/dist/styles/text-layout.css +134 -15
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/activity.css +13 -4
- package/dist/tokens/components/affix.css +7 -0
- package/dist/tokens/components/anchor.css +17 -0
- package/dist/tokens/components/attachments.css +1 -1
- package/dist/tokens/components/badge.css +1 -1
- package/dist/tokens/components/card.css +28 -7
- package/dist/tokens/components/chart.css +4 -1
- package/dist/tokens/components/chat-composer.css +4 -1
- package/dist/tokens/components/control.css +72 -33
- package/dist/tokens/components/conversations.css +4 -1
- package/dist/tokens/components/data-display.css +42 -15
- package/dist/tokens/components/data-entry.css +8 -2
- package/dist/tokens/components/descriptions.css +1 -1
- package/dist/tokens/components/feedback.css +8 -5
- package/dist/tokens/components/float-button.css +8 -2
- package/dist/tokens/components/form.css +1 -1
- package/dist/tokens/components/legal-document.css +12 -3
- package/dist/tokens/components/logo.css +15 -6
- package/dist/tokens/components/marquee.css +7 -0
- package/dist/tokens/components/masonry.css +6 -0
- package/dist/tokens/components/mega-menu.css +71 -0
- package/dist/tokens/components/navigation.css +37 -13
- package/dist/tokens/components/segmented.css +4 -1
- package/dist/tokens/components/separator.css +4 -1
- package/dist/tokens/components/shell.css +99 -31
- package/dist/tokens/components/table.css +11 -5
- package/dist/tokens/components/thought-chain.css +4 -1
- package/dist/tokens/components/toggle.css +4 -1
- package/dist/tokens/components/tree.css +1 -1
- package/dist/tokens/components/upload.css +21 -9
- package/dist/tokens/foundation.css +35 -30
- package/dist/tokens/semantic/layout.css +26 -5
- 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/TOKENS.md +16 -1
- 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/case4-login.tsx +10 -2
- package/docs/showcase/case5-shift-calendar.tsx +1 -1
- package/docs/showcase/case6-agency-handy.tsx +6 -6
- package/docs/showcase/futurelastic-web.tsx +89 -49
- package/docs/showcase/marketing-page.tsx +885 -0
- package/docs/showcase/table-expandable-rows.tsx +4 -1
- package/docs/showcase/table-footer-totals.tsx +12 -2
- package/docs/showcase/theme-customization.tsx +1259 -0
- package/package.json +6 -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>
|
|
@@ -283,11 +283,19 @@ export default function LoginShowcase() {
|
|
|
283
283
|
|
|
284
284
|
<Text as="p" size="2xs" tone="muted" align="center" className="leading-relaxed">
|
|
285
285
|
ログインすると{" "}
|
|
286
|
-
<Button
|
|
286
|
+
<Button
|
|
287
|
+
variant="link"
|
|
288
|
+
size="sm"
|
|
289
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))]"
|
|
290
|
+
>
|
|
287
291
|
利用規約
|
|
288
292
|
</Button>{" "}
|
|
289
293
|
·{" "}
|
|
290
|
-
<Button
|
|
294
|
+
<Button
|
|
295
|
+
variant="link"
|
|
296
|
+
size="sm"
|
|
297
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))]"
|
|
298
|
+
>
|
|
291
299
|
プライバシーポリシー
|
|
292
300
|
</Button>{" "}
|
|
293
301
|
に同意したものとみなされます。
|
|
@@ -588,7 +588,7 @@ function MonthGrid({ onPick }: { onPick: (d: DayCell) => void }) {
|
|
|
588
588
|
tone="destructive"
|
|
589
589
|
variant="outline"
|
|
590
590
|
shape="sharp"
|
|
591
|
-
className="text-[length:var(--font-size-2xs)]"
|
|
591
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))]"
|
|
592
592
|
>
|
|
593
593
|
祝
|
|
594
594
|
</Badge>
|
|
@@ -368,7 +368,7 @@ function TabBar({ active, onChange }: { active: string; onChange: (id: string) =
|
|
|
368
368
|
onClick={() => onChange(t.id)}
|
|
369
369
|
aria-current={isActive ? "page" : undefined}
|
|
370
370
|
className={
|
|
371
|
-
"
|
|
371
|
+
"text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))] h-full flex-col rounded-none font-medium " +
|
|
372
372
|
(isActive ? "text-primary" : "text-muted-foreground")
|
|
373
373
|
}
|
|
374
374
|
>
|
|
@@ -637,7 +637,7 @@ function InboundTab({
|
|
|
637
637
|
<ToggleGroupItem
|
|
638
638
|
key={f.id}
|
|
639
639
|
value={f.id}
|
|
640
|
-
className="
|
|
640
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 shrink-0 rounded-full whitespace-nowrap"
|
|
641
641
|
// The library's OWN counter pill, not a `Text` at 70% opacity (gh#643). The
|
|
642
642
|
// hand-rolled one measured 2.15:1 on the selected chip's --primary fill, because
|
|
643
643
|
// `opacity-70` blends whatever ink it inherits back towards the fill underneath it —
|
|
@@ -852,19 +852,19 @@ function OutboundTab({ seg, setSeg }: { seg: string; setSeg: (v: string) => void
|
|
|
852
852
|
<ResponsiveGrid columns={3} gap="xs">
|
|
853
853
|
<ToggleGroupItem
|
|
854
854
|
value="seal"
|
|
855
|
-
className="
|
|
855
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 rounded-xl whitespace-nowrap"
|
|
856
856
|
>
|
|
857
857
|
Chờ niêm phong
|
|
858
858
|
</ToggleGroupItem>
|
|
859
859
|
<ToggleGroupItem
|
|
860
860
|
value="handoff"
|
|
861
|
-
className="
|
|
861
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 rounded-xl whitespace-nowrap"
|
|
862
862
|
>
|
|
863
863
|
Chờ bàn giao
|
|
864
864
|
</ToggleGroupItem>
|
|
865
865
|
<ToggleGroupItem
|
|
866
866
|
value="done"
|
|
867
|
-
className="
|
|
867
|
+
className="text-[length:var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)))] h-9 rounded-xl whitespace-nowrap"
|
|
868
868
|
>
|
|
869
869
|
Đã bàn giao
|
|
870
870
|
</ToggleGroupItem>
|
|
@@ -1018,7 +1018,7 @@ export default function AgencyHandyShowcase() {
|
|
|
1018
1018
|
<ToggleGroupItem
|
|
1019
1019
|
key={s}
|
|
1020
1020
|
value={s}
|
|
1021
|
-
className="
|
|
1021
|
+
className="text-[length:var(--font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)))] size-7 rounded-md uppercase"
|
|
1022
1022
|
title={s}
|
|
1023
1023
|
>
|
|
1024
1024
|
{s[0]}
|
|
@@ -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,30 @@ 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
|
-
|
|
58
|
+
/* 80px hero, from the display ramp's BASE KNOB alone (gh#834).
|
|
59
|
+
This used to need a second line — --font-size-5xl: 5rem — and the comment that stood here said
|
|
60
|
+
why: --font-size-5xl was declared at :root as var(--font-size-display), so it substituted on
|
|
61
|
+
<html> and this scope merely INHERITED the answer root had given. Heading size="5xl" painted
|
|
62
|
+
54px with the display knob alone. The step is an initial knob now and the formula sits at the
|
|
63
|
+
call site, so one line moves the ramp: re-measured here at 80px, with 3xl/4xl following to
|
|
64
|
+
48.83/62.5px. */
|
|
65
|
+
--font-size-display: 5rem;
|
|
66
|
+
/* The glass bar, opted into once (gh#831). acme-website.tsx hand-wrote the identical pair,
|
|
67
|
+
which is why these are framework knobs now and not two literals on .fl-navbar. */
|
|
68
|
+
--topbar-background-alpha: 80%;
|
|
69
|
+
--topbar-backdrop-blur-size: 12px;
|
|
58
70
|
color-scheme: dark;
|
|
59
71
|
}
|
|
60
72
|
/* Marketing surface (consumer section stylesheet) */
|
|
61
73
|
[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
|
-
|
|
74
|
+
[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; }
|
|
75
|
+
[data-tenant="futurelastic"] .fl-section { padding-block: var(--space-section-band); }
|
|
76
|
+
/* The sticky header IS this page's bar region, so it reads the --topbar-* glass knobs. See the
|
|
77
|
+
matching note in acme-website.tsx for why it is not a <Topbar>. */
|
|
78
|
+
[data-tenant="futurelastic"] .fl-navbar { position: sticky; inset-block-start: 0; z-index: 30;
|
|
79
|
+
background-color: hsl(var(--background) / var(--topbar-background-alpha));
|
|
80
|
+
backdrop-filter: blur(var(--topbar-backdrop-blur-size));
|
|
81
|
+
border-block-end: var(--stroke-hairline) solid hsl(var(--border)); }
|
|
66
82
|
/* REFLOW, NOT DECORATION (gh#643). This row was a fixed 68px flex line with no wrap, so at 320 its
|
|
67
83
|
content ran to x=569.6 inside a 320px nav: every link painted OUTSIDE the navbar's own
|
|
68
84
|
translucent box, over the page behind it, and axe read the white labels against the light
|
|
@@ -71,53 +87,57 @@ const THEME = `
|
|
|
71
87
|
element looks like from the outside. min-height rather than height, so a wrapped row may be
|
|
72
88
|
taller than one line and the language cluster stops overflowing the bar vertically (measured at
|
|
73
89
|
320: it was 148.3px tall, starting at y=-40.2). */
|
|
74
|
-
[data-tenant="futurelastic"] .fl-navbar-inner { min-
|
|
90
|
+
[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
91
|
[data-tenant="futurelastic"] .fl-brand { font-family: var(--font-family-display); font-weight: 700; font-size: 1.25rem; color: hsl(var(--foreground)); }
|
|
76
92
|
[data-tenant="futurelastic"] .fl-eyebrow { font-family: var(--font-family-body); font-weight: 600; font-size: 0.75rem;
|
|
77
93
|
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:
|
|
94
|
+
[data-tenant="futurelastic"] .fl-badge { display: inline-flex; align-items: center; gap: var(--space-2); padding: 0.375rem 0.875rem;
|
|
95
|
+
border: var(--stroke-hairline) solid hsl(var(--primary) / 0.3); background: hsl(var(--primary) / 0.08); border-radius: var(--radius-pill);
|
|
80
96
|
font-size: 0.8125rem; font-weight: 500; color: hsl(var(--foreground)); }
|
|
81
|
-
[data-tenant="futurelastic"] .fl-badge .dot {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
[data-tenant="futurelastic"] .fl-badge .dot { inline-size: 6px; block-size: 6px; border-radius: var(--radius-pill); background: hsl(var(--primary)); }
|
|
98
|
+
/* The hero band still breathes with the viewport, but both ends of the clamp are now the band/hero
|
|
99
|
+
steps rather than an eyeballed 5rem…10rem (gh#831). */
|
|
100
|
+
[data-tenant="futurelastic"] .fl-hero { position: relative; overflow: hidden; padding-block: clamp(var(--space-section-band), 3rem + 9vw, var(--space-section-hero)); }
|
|
101
|
+
/* The halo is .ui-brand-glow (src/styles/layout.css), which ships and was going unused. This
|
|
102
|
+
class and .fl-cta-glow now only place it and set its documented knobs. */
|
|
103
|
+
[data-tenant="futurelastic"] .fl-hero-glow { position: absolute; inset: 0;
|
|
104
|
+
--brand-glow-color: 42 54% 54%; --brand-glow-size: 60% 50%; --brand-glow-position: 70% 0%; --brand-glow-alpha: 0.12; }
|
|
85
105
|
[data-tenant="futurelastic"] .fl-hero-inner { position: relative; max-width: 860px; }
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
/* .fl-display / .fl-h2 / .fl-lead / .fl-hero-glow / .fl-cta-glow are GONE (gh#826) — and the fact
|
|
107
|
+
that this file and acme-website.tsx, two unrelated brands, had independently written the SAME
|
|
108
|
+
five is what made them a missing API rather than brand styling. They are now
|
|
109
|
+
Heading size="5xl", Heading size="4xl", Text size="xl" and two .ui-brand-glow
|
|
110
|
+
layers. The display face still arrives through --font-family-display (base.css points every
|
|
111
|
+
<h1>-<h6> at it); the tracking is a token, set once above. */
|
|
90
112
|
[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; }
|
|
113
|
+
[data-tenant="futurelastic"] .fl-note { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); margin-block-start: var(--space-10); }
|
|
93
114
|
[data-tenant="futurelastic"] .fl-head { max-width: 640px; margin-inline: auto; text-align: center; }
|
|
94
|
-
[data-tenant="futurelastic"] .fl-hero-actions { margin-
|
|
115
|
+
[data-tenant="futurelastic"] .fl-hero-actions { margin-block-start: 1.75rem; }
|
|
95
116
|
[data-tenant="futurelastic"] .fl-cta-inner { position: relative; max-width: 680px; margin-inline: auto; }
|
|
96
117
|
[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:
|
|
118
|
+
[data-tenant="futurelastic"] .fl-logocloud { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-4) var(--space-10); }
|
|
119
|
+
[data-tenant="futurelastic"] .fl-logo-item { display: inline-flex; align-items: center; gap: var(--space-2); font-family: var(--font-family-display);
|
|
99
120
|
font-weight: 600; font-size: 1.0625rem; color: hsl(var(--foreground) / 0.85); }
|
|
100
121
|
[data-tenant="futurelastic"] .fl-logo-item .tag { font-family: var(--font-family-body); font-weight: 500; font-size: 0.6875rem;
|
|
101
122
|
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:
|
|
123
|
+
[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); }
|
|
124
|
+
[data-tenant="futurelastic"] .fl-bento { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
|
|
104
125
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento { grid-template-columns: repeat(6, 1fr); } }
|
|
105
126
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento > .fl-cell { grid-column: span 2; } }
|
|
106
127
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-bento > .fl-cell.wide { grid-column: span 4; } }
|
|
107
128
|
@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; }
|
|
129
|
+
[data-tenant="futurelastic"] .fl-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-8); }
|
|
110
130
|
@container (min-width: 768px) { [data-tenant="futurelastic"] .fl-stats { grid-template-columns: repeat(4, 1fr); } }
|
|
111
131
|
[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:
|
|
132
|
+
[data-tenant="futurelastic"] .fl-cta { position: relative; overflow: hidden; text-align: center; border: var(--stroke-hairline) solid hsl(var(--primary) / 0.2);
|
|
133
|
+
border-radius: var(--radius-2xl); background: hsl(var(--card)); padding: 4rem var(--space-8); }
|
|
134
|
+
[data-tenant="futurelastic"] .fl-cta-glow { position: absolute; inset: 0;
|
|
135
|
+
--brand-glow-color: 42 54% 54%; --brand-glow-size: 60% 80%; --brand-glow-position: 50% 0%; --brand-glow-alpha: 0.14; }
|
|
136
|
+
[data-tenant="futurelastic"] .fl-footer { border-block-start: var(--stroke-hairline) solid hsl(var(--border)); padding-block: 3.5rem var(--space-8); }
|
|
137
|
+
[data-tenant="futurelastic"] .fl-footer-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-10); }
|
|
118
138
|
@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-
|
|
139
|
+
[data-tenant="futurelastic"] .fl-footer-bottom { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-4);
|
|
140
|
+
margin-block-start: var(--space-10); padding-block-start: var(--space-6); border-block-start: var(--stroke-hairline) solid hsl(var(--border)); }
|
|
121
141
|
`;
|
|
122
142
|
|
|
123
143
|
const SHELL = "fl-shell";
|
|
@@ -151,18 +171,26 @@ function Navbar() {
|
|
|
151
171
|
function Hero() {
|
|
152
172
|
return (
|
|
153
173
|
<header className="fl-hero">
|
|
154
|
-
<div
|
|
174
|
+
<div
|
|
175
|
+
className="ui-brand-glow absolute inset-0"
|
|
176
|
+
style={
|
|
177
|
+
{
|
|
178
|
+
"--brand-glow-alpha": "0.12",
|
|
179
|
+
"--brand-glow-position": "70% 0%",
|
|
180
|
+
} as React.CSSProperties
|
|
181
|
+
}
|
|
182
|
+
/>
|
|
155
183
|
<div className={`${SHELL} fl-hero-inner`}>
|
|
156
184
|
<span className="fl-badge">
|
|
157
185
|
<span className="dot" /> Tech · AI · Holdings
|
|
158
186
|
</span>
|
|
159
|
-
<
|
|
187
|
+
<Heading level={1} size="5xl" weight="bold">
|
|
160
188
|
Building the <span className="fl-gold">elastic</span> infrastructure of the future.
|
|
161
|
-
</
|
|
162
|
-
<p
|
|
189
|
+
</Heading>
|
|
190
|
+
<Text as="p" size="xl" tone="muted">
|
|
163
191
|
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
192
|
Bản và Việt Nam, từ logistics, fintech đến F&B và thương mại.
|
|
165
|
-
</
|
|
193
|
+
</Text>
|
|
166
194
|
<Flex direction="row" gap="md" className="fl-hero-actions">
|
|
167
195
|
<Button size="lg">Khám phá hệ sinh thái</Button>
|
|
168
196
|
<Button variant="outline" size="lg">
|
|
@@ -201,11 +229,11 @@ function LogoCloud() {
|
|
|
201
229
|
);
|
|
202
230
|
}
|
|
203
231
|
|
|
204
|
-
function Medallion({ icon:
|
|
232
|
+
function Medallion({ icon: Glyph }: { icon: typeof Boxes }) {
|
|
205
233
|
return (
|
|
206
234
|
<Avatar className="rounded-lg">
|
|
207
|
-
<AvatarFallback className="bg-primary/10 text-primary
|
|
208
|
-
<Icon
|
|
235
|
+
<AvatarFallback className="bg-primary/10 text-primary rounded-lg">
|
|
236
|
+
<Icon as={Glyph} size="xl" />
|
|
209
237
|
</AvatarFallback>
|
|
210
238
|
</Avatar>
|
|
211
239
|
);
|
|
@@ -247,10 +275,12 @@ function Bento() {
|
|
|
247
275
|
<Flex direction="col" gap="xl" className={SHELL}>
|
|
248
276
|
<div className="fl-head">
|
|
249
277
|
<div className="fl-eyebrow">Hệ sinh thái</div>
|
|
250
|
-
<
|
|
251
|
-
|
|
278
|
+
<Heading level={2} size="4xl" weight="bold">
|
|
279
|
+
Một mái nhà, nhiều mũi nhọn
|
|
280
|
+
</Heading>
|
|
281
|
+
<Text as="p" size="xl" tone="muted" className="mx-auto">
|
|
252
282
|
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
|
-
</
|
|
283
|
+
</Text>
|
|
254
284
|
</div>
|
|
255
285
|
<div className="fl-bento">
|
|
256
286
|
{cells.map((c) => (
|
|
@@ -304,13 +334,23 @@ function Cta() {
|
|
|
304
334
|
<section className="fl-section">
|
|
305
335
|
<div className={SHELL}>
|
|
306
336
|
<div className="fl-cta">
|
|
307
|
-
<div
|
|
337
|
+
<div
|
|
338
|
+
className="ui-brand-glow absolute inset-0"
|
|
339
|
+
style={
|
|
340
|
+
{
|
|
341
|
+
"--brand-glow-alpha": "0.14",
|
|
342
|
+
"--brand-glow-size": "60% 80%",
|
|
343
|
+
} as React.CSSProperties
|
|
344
|
+
}
|
|
345
|
+
/>
|
|
308
346
|
<Flex direction="col" gap="xl" className="fl-cta-inner">
|
|
309
|
-
<
|
|
310
|
-
|
|
347
|
+
<Heading level={2} size="4xl" weight="bold">
|
|
348
|
+
Cùng xây điều mới?
|
|
349
|
+
</Heading>
|
|
350
|
+
<Text as="p" size="xl" tone="muted" className="mx-auto">
|
|
311
351
|
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
352
|
F&B tại Nhật Bản & Việt Nam.
|
|
313
|
-
</
|
|
353
|
+
</Text>
|
|
314
354
|
<Flex direction="row" gap="md" className="justify-center">
|
|
315
355
|
<Button size="lg">Đặt lịch trao đổi</Button>
|
|
316
356
|
<Button variant="ghost" size="lg">
|