@hraness/design-kit 0.31.1
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/ARTICLE_COPY.md +208 -0
- package/HERO_FIELDS.md +55 -0
- package/ICONS.md +56 -0
- package/LANTERN_MATERIAL.md +60 -0
- package/LICENSE +21 -0
- package/MARKETING_COPY.md +49 -0
- package/MARKETING_PRESET.md +108 -0
- package/PALETTES.md +90 -0
- package/PAPER_THEME.md +128 -0
- package/README.md +745 -0
- package/STATUS_PAGES.md +67 -0
- package/STYLE.md +245 -0
- package/WRITING.md +72 -0
- package/dist/browser/index.js +2345 -0
- package/dist/chunk-44djxk16.js +6872 -0
- package/dist/chunk-5gtx3pza.js +9 -0
- package/dist/chunk-77391vmq.js +454 -0
- package/dist/chunk-7b1586eb.js +1209 -0
- package/dist/chunk-8834fh4n.js +6 -0
- package/dist/chunk-9t8xyqte.js +870 -0
- package/dist/chunk-cejpzyfh.js +446 -0
- package/dist/chunk-eh71jz57.js +429 -0
- package/dist/chunk-evb02bc1.js +516 -0
- package/dist/chunk-h4k7yv6x.js +368 -0
- package/dist/chunk-he8eznb1.js +760 -0
- package/dist/chunk-mr1vdcjq.js +404 -0
- package/dist/chunk-wzvdn8ey.js +116 -0
- package/dist/fonts/nebula-sans/social-fonts.generated.js +53 -0
- package/dist/icons.js +48 -0
- package/dist/index.js +237 -0
- package/dist/launch.js +41 -0
- package/dist/mockups/client.js +422 -0
- package/dist/mockups/index.js +1850 -0
- package/dist/portfolio.js +1549 -0
- package/dist/provider-marks.js +19 -0
- package/dist/react/charts.js +11 -0
- package/dist/react/hero-backdrop.js +8 -0
- package/dist/react/index.js +7252 -0
- package/dist/react/platform-install.js +10 -0
- package/dist/react/server.js +137 -0
- package/dist/stylex-manifest.json +1 -0
- package/dist/stylex.css +5392 -0
- package/dist/syntax-highlighting.js +15 -0
- package/dist/testing.js +250 -0
- package/package.json +377 -0
- package/portfolio-inventory.json +31 -0
- package/scripts/check-lantern-material-snapshot.d.mts +26 -0
- package/scripts/check-lantern-material-snapshot.mjs +126 -0
- package/scripts/check-marketing-snapshot.d.mts +12 -0
- package/scripts/check-marketing-snapshot.mjs +58 -0
- package/scripts/lantern-material-snapshot.ts +101 -0
- package/scripts/marketing-textures.ts +48 -0
- package/scripts/paper-theme-snapshot.ts +97 -0
- package/scripts/product-marketing-snapshot.ts +149 -0
- package/src/appearance-menu.css +266 -0
- package/src/appearance.ts +32 -0
- package/src/article-html.ts +269 -0
- package/src/article.ts +686 -0
- package/src/browser/appearance-menu.ts +604 -0
- package/src/browser/artifact-share.ts +164 -0
- package/src/browser/design-palette.ts +197 -0
- package/src/browser/foil.ts +124 -0
- package/src/browser/hero-light.ts +11 -0
- package/src/browser/index.ts +29 -0
- package/src/browser/status-field.ts +432 -0
- package/src/browser/status-page.ts +100 -0
- package/src/browser/sticky-offset.ts +82 -0
- package/src/browser/theme-color-sync.ts +213 -0
- package/src/charts.css +7 -0
- package/src/compiler-components.css +34 -0
- package/src/compiler-foundation.css +18 -0
- package/src/compiler-palettes.css +7 -0
- package/src/compiler-tokens.css +342 -0
- package/src/components.css +3 -0
- package/src/design-gallery.css +431 -0
- package/src/effects.css +14 -0
- package/src/fonts/geist-mono/GeistMono[wght].woff2 +0 -0
- package/src/fonts/geist-mono/OFL.txt +93 -0
- package/src/fonts/geist-mono/PROVENANCE.md +12 -0
- package/src/fonts/instrument-serif/OFL.txt +93 -0
- package/src/fonts/instrument-serif/UPSTREAM.md +14 -0
- package/src/fonts/instrument-serif/instrument-serif-latin-400.woff2 +0 -0
- package/src/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/fonts/nebula-sans/NebulaSans-Black.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-BlackItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-BoldItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-BookItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Light.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-LightItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Medium.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-MediumItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Semibold.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-SemiboldItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/fonts/nebula-sans/social-fonts.generated.ts +3873 -0
- package/src/fonts.css +106 -0
- package/src/icons/act60/act60.svg +5 -0
- package/src/icons/act60/application.svg +9 -0
- package/src/icons/act60/chapter-compare.svg +3 -0
- package/src/icons/act60/decree-residence.svg +6 -0
- package/src/icons/act60/estimate.svg +7 -0
- package/src/icons/act60/export-services.svg +4 -0
- package/src/icons/act60/investor.svg +13 -0
- package/src/icons/act60/presence.svg +7 -0
- package/src/icons/aicharts/aicharts.svg +6 -0
- package/src/icons/aicharts/cost-compare.svg +4 -0
- package/src/icons/aicharts/task-audio.svg +7 -0
- package/src/icons/aicharts/task-coding.svg +6 -0
- package/src/icons/aicharts/task-images.svg +3 -0
- package/src/icons/aicharts/task-reasoning.svg +9 -0
- package/src/icons/aicharts/task-research.svg +5 -0
- package/src/icons/aicharts/task-video.svg +4 -0
- package/src/icons/manifest.json +1371 -0
- package/src/icons/platonik/conversation.svg +3 -0
- package/src/icons/platonik/creature.svg +7 -0
- package/src/icons/platonik/economy.svg +4 -0
- package/src/icons/platonik/keep-light.svg +6 -0
- package/src/icons/platonik/platonik.svg +7 -0
- package/src/icons/platonik/spark-route.svg +15 -0
- package/src/icons/receipts/act60-retry.receipt.json +888 -0
- package/src/icons/receipts/act60.receipt.json +1366 -0
- package/src/icons/receipts/aicharts.receipt.json +1546 -0
- package/src/icons/receipts/platonik.receipt.json +1011 -0
- package/src/icons/receipts/roughday.receipt.json +770 -0
- package/src/icons/receipts/shared.receipt.json +1296 -0
- package/src/icons/receipts/slopcamera.receipt.json +1495 -0
- package/src/icons/receipts/soundfish-retry.receipt.json +460 -0
- package/src/icons/receipts/soundfish.receipt.json +1709 -0
- package/src/icons/receipts/sponge.receipt.json +1743 -0
- package/src/icons/receipts/stripe-history-retry.receipt.json +145 -0
- package/src/icons/receipts/stripe-history.receipt.json +1202 -0
- package/src/icons/receipts/wordcell.receipt.json +1924 -0
- package/src/icons/roughday/finance.svg +7 -0
- package/src/icons/roughday/roughday.svg +6 -0
- package/src/icons/roughday/technology.svg +4 -0
- package/src/icons/roughday/world.svg +4 -0
- package/src/icons/sets/act60.json +63 -0
- package/src/icons/sets/aicharts.json +63 -0
- package/src/icons/sets/platonik.json +39 -0
- package/src/icons/sets/roughday.json +25 -0
- package/src/icons/sets/shared.json +12 -0
- package/src/icons/sets/slopcamera.json +47 -0
- package/src/icons/sets/soundfish.json +43 -0
- package/src/icons/sets/sponge.json +51 -0
- package/src/icons/sets/stripe-history.json +39 -0
- package/src/icons/sets/wordcell.json +51 -0
- package/src/icons/shared/agent-skill.svg +4 -0
- package/src/icons/shared/cli.svg +5 -0
- package/src/icons/shared/privacy.svg +3 -0
- package/src/icons/shared/research.svg +7 -0
- package/src/icons/shared/sdk.svg +3 -0
- package/src/icons/slopcamera/diagram.svg +3 -0
- package/src/icons/slopcamera/direct-scene.svg +5 -0
- package/src/icons/slopcamera/edit-video.svg +17 -0
- package/src/icons/slopcamera/mcp.svg +3 -0
- package/src/icons/slopcamera/native-world.svg +3 -0
- package/src/icons/slopcamera/slopcamera.svg +11 -0
- package/src/icons/soundfish/agent-ui.svg +7 -0
- package/src/icons/soundfish/collaborate.svg +5 -0
- package/src/icons/soundfish/compose.svg +3 -0
- package/src/icons/soundfish/hear.svg +6 -0
- package/src/icons/soundfish/human-ui.svg +6 -0
- package/src/icons/soundfish/share.svg +3 -0
- package/src/icons/soundfish/soundfish.svg +6 -0
- package/src/icons/sponge/api.svg +3 -0
- package/src/icons/sponge/documents.svg +3 -0
- package/src/icons/sponge/library.svg +3 -0
- package/src/icons/sponge/one-question.svg +5 -0
- package/src/icons/sponge/people-direct.svg +5 -0
- package/src/icons/sponge/read.svg +3 -0
- package/src/icons/sponge/save.svg +4 -0
- package/src/icons/sponge/sponge.svg +7 -0
- package/src/icons/stripe-history/company-history.svg +3 -0
- package/src/icons/stripe-history/evidence.svg +4 -0
- package/src/icons/stripe-history/independence.svg +7 -0
- package/src/icons/stripe-history/publications.svg +11 -0
- package/src/icons/stripe-history/sources.svg +6 -0
- package/src/icons/stripe-history/stripe-history.svg +3 -0
- package/src/icons/wordcell/backlinks.svg +6 -0
- package/src/icons/wordcell/capture.svg +6 -0
- package/src/icons/wordcell/git-provenance.svg +4 -0
- package/src/icons/wordcell/kb.svg +4 -0
- package/src/icons/wordcell/markdown.svg +4 -0
- package/src/icons/wordcell/scopes.svg +3 -0
- package/src/icons/wordcell/search.svg +7 -0
- package/src/icons.generated.ts +734 -0
- package/src/icons.ts +51 -0
- package/src/index.ts +275 -0
- package/src/lantern-material.css +213 -0
- package/src/launch.ts +624 -0
- package/src/marketing-assets/UPSTREAM.md +18 -0
- package/src/marketing-assets/cells.svg +1 -0
- package/src/marketing-assets/grain.svg +1 -0
- package/src/marketing-forced-colors.css +12 -0
- package/src/mockups/client.tsx +415 -0
- package/src/mockups/core.tsx +318 -0
- package/src/mockups/frames.tsx +368 -0
- package/src/mockups/index.ts +14 -0
- package/src/mockups/surfaces.tsx +681 -0
- package/src/mockups.css +2607 -0
- package/src/palette-appearance.ts +45 -0
- package/src/palette-bridge.css +124 -0
- package/src/palette-color.ts +41 -0
- package/src/palette-system.css +721 -0
- package/src/palette-themes.ts +23 -0
- package/src/palette-tokens.stylex.ts +519 -0
- package/src/palettes.css +3 -0
- package/src/palettes.ts +161 -0
- package/src/paper-theme.css +244 -0
- package/src/plain-publication.css +1241 -0
- package/src/plain-site.css +397 -0
- package/src/platforms.ts +151 -0
- package/src/portfolio.generated.json +1595 -0
- package/src/portfolio.generated.ts +1596 -0
- package/src/portfolio.ts +226 -0
- package/src/product-marketing-foundation.css +433 -0
- package/src/product-marketing-preset.css +261 -0
- package/src/product-marketing.css +2186 -0
- package/src/provider-marks.generated.ts +112 -0
- package/src/provider-marks.ts +143 -0
- package/src/react/animated-rail-stage.stylex.ts +15 -0
- package/src/react/animated-rail-stage.tsx +51 -0
- package/src/react/app-shell.stylex.ts +104 -0
- package/src/react/app-shell.tsx +121 -0
- package/src/react/article.tsx +602 -0
- package/src/react/aurora-dots-background.tsx +34 -0
- package/src/react/charts.stylex.ts +563 -0
- package/src/react/charts.tsx +610 -0
- package/src/react/chat.stylex.ts +34 -0
- package/src/react/chat.tsx +155 -0
- package/src/react/design-gallery.tsx +943 -0
- package/src/react/design-palette.stylex.ts +124 -0
- package/src/react/design-palette.tsx +196 -0
- package/src/react/design-theme-context.tsx +41 -0
- package/src/react/effects.stylex.ts +325 -0
- package/src/react/fader.stylex.ts +71 -0
- package/src/react/fader.tsx +151 -0
- package/src/react/foil-card-math.ts +100 -0
- package/src/react/foil-card-surface.stylex.ts +326 -0
- package/src/react/foil-card-surface.tsx +742 -0
- package/src/react/foil-mark.tsx +35 -0
- package/src/react/foil.stylex.ts +163 -0
- package/src/react/haptics.ts +228 -0
- package/src/react/hero-backdrop.tsx +22 -0
- package/src/react/index.ts +38 -0
- package/src/react/keyboard-shortcuts.ts +228 -0
- package/src/react/lantern-material-gallery.tsx +166 -0
- package/src/react/lantern-material.stylex.ts +39 -0
- package/src/react/launch-beats.tsx +73 -0
- package/src/react/navigation-rail.stylex.ts +130 -0
- package/src/react/navigation-rail.tsx +228 -0
- package/src/react/particle-halo.tsx +115 -0
- package/src/react/phaser-dots.tsx +501 -0
- package/src/react/platform-icons.tsx +94 -0
- package/src/react/platform-install.stylex.ts +385 -0
- package/src/react/platform-install.tsx +389 -0
- package/src/react/playback-transport.stylex.ts +14 -0
- package/src/react/playback-transport.tsx +112 -0
- package/src/react/procedural-backdrop.tsx +192 -0
- package/src/react/procedural-recipe.ts +323 -0
- package/src/react/product-marketing.stylex.ts +4469 -0
- package/src/react/product-marketing.tsx +1611 -0
- package/src/react/production-data-preview-notice.stylex.ts +40 -0
- package/src/react/production-data-preview-notice.tsx +34 -0
- package/src/react/provider-mark.stylex.ts +132 -0
- package/src/react/provider-mark.tsx +120 -0
- package/src/react/relative-time.tsx +117 -0
- package/src/react/route-state.stylex.ts +39 -0
- package/src/react/route-state.tsx +350 -0
- package/src/react/server.ts +11 -0
- package/src/react/social-kit-panel.tsx +103 -0
- package/src/react/sticky-offset.tsx +33 -0
- package/src/react/surfaces.stylex.ts +179 -0
- package/src/react/surfaces.tsx +330 -0
- package/src/react/syntax-code.tsx +33 -0
- package/src/react/theme-color-sync.ts +1 -0
- package/src/react/theme-resolution.ts +11 -0
- package/src/react/theme.stylex.ts +276 -0
- package/src/react/theme.tsx +402 -0
- package/src/reading.css +72 -0
- package/src/relative-time.ts +185 -0
- package/src/reset.css +2 -0
- package/src/site-shell.css +16 -0
- package/src/status-page-html.ts +63 -0
- package/src/status-page.css +276 -0
- package/src/status-page.ts +323 -0
- package/src/styles.css +21 -0
- package/src/syntax-code-lexers.ts +303 -0
- package/src/syntax-highlighting.css +78 -0
- package/src/syntax-highlighting.ts +477 -0
- package/src/syntax-tokens.ts +33 -0
- package/src/testing.ts +307 -0
- package/src/tokens.css +3 -0
- package/src/typography.css +86 -0
- package/vendor/evilcharts/LICENSE +21 -0
- package/vendor/evilcharts/UPSTREAM.md +14 -0
- package/vendor/platform-marks/LICENSE +30 -0
- package/vendor/platform-marks/UPSTREAM.md +15 -0
- package/vendor/platform-marks/apple.svg +1 -0
- package/vendor/platform-marks/linux.svg +1 -0
- package/vendor/provider-marks/CRUSH-LICENSE.md +134 -0
- package/vendor/provider-marks/LICENSE +21 -0
- package/vendor/provider-marks/UPSTREAM.md +27 -0
- package/vendor/provider-marks/aider.svg +3 -0
- package/vendor/provider-marks/crush-heartbit.svg +8 -0
- package/vendor/provider-marks/lobehub/alibabacloud-color.svg +1 -0
- package/vendor/provider-marks/lobehub/alibabacloud.svg +1 -0
- package/vendor/provider-marks/lobehub/amp-color.svg +1 -0
- package/vendor/provider-marks/lobehub/amp.svg +1 -0
- package/vendor/provider-marks/lobehub/anthropic.svg +1 -0
- package/vendor/provider-marks/lobehub/claude-color.svg +1 -0
- package/vendor/provider-marks/lobehub/claude.svg +1 -0
- package/vendor/provider-marks/lobehub/claudecode-color.svg +1 -0
- package/vendor/provider-marks/lobehub/claudecode.svg +1 -0
- package/vendor/provider-marks/lobehub/codex-color.svg +1 -0
- package/vendor/provider-marks/lobehub/codex.svg +1 -0
- package/vendor/provider-marks/lobehub/cursor.svg +1 -0
- package/vendor/provider-marks/lobehub/deepseek-color.svg +1 -0
- package/vendor/provider-marks/lobehub/deepseek.svg +1 -0
- package/vendor/provider-marks/lobehub/devin-color.svg +1 -0
- package/vendor/provider-marks/lobehub/devin.svg +1 -0
- package/vendor/provider-marks/lobehub/gemini-color.svg +1 -0
- package/vendor/provider-marks/lobehub/gemini.svg +1 -0
- package/vendor/provider-marks/lobehub/geminicli-color.svg +1 -0
- package/vendor/provider-marks/lobehub/geminicli.svg +1 -0
- package/vendor/provider-marks/lobehub/goose.svg +1 -0
- package/vendor/provider-marks/lobehub/meta-color.svg +1 -0
- package/vendor/provider-marks/lobehub/meta.svg +1 -0
- package/vendor/provider-marks/lobehub/mistral-color.svg +1 -0
- package/vendor/provider-marks/lobehub/mistral.svg +1 -0
- package/vendor/provider-marks/lobehub/moonshot.svg +1 -0
- package/vendor/provider-marks/lobehub/nvidia-color.svg +1 -0
- package/vendor/provider-marks/lobehub/nvidia.svg +1 -0
- package/vendor/provider-marks/lobehub/openai.svg +1 -0
- package/vendor/provider-marks/lobehub/opencode.svg +1 -0
- package/vendor/provider-marks/lobehub/perplexity-color.svg +1 -0
- package/vendor/provider-marks/lobehub/perplexity.svg +1 -0
- package/vendor/provider-marks/lobehub/qwen-color.svg +1 -0
- package/vendor/provider-marks/lobehub/qwen.svg +1 -0
- package/vendor/provider-marks/lobehub/xai.svg +1 -0
- package/vendor/provider-marks/lobehub/zai.svg +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as stylex from "@stylexjs/stylex";
|
|
2
|
+
|
|
3
|
+
export const productionDataPreviewNoticeStyles = stylex.create({
|
|
4
|
+
emphasis: {
|
|
5
|
+
fontWeight: "var(--font-weight-bold, 700)",
|
|
6
|
+
letterSpacing: "0.04em",
|
|
7
|
+
textTransform: "uppercase",
|
|
8
|
+
},
|
|
9
|
+
root: {
|
|
10
|
+
alignItems: "center",
|
|
11
|
+
backgroundColor: "#ffcc33",
|
|
12
|
+
backgroundAttachment: "scroll",
|
|
13
|
+
backgroundClip: "border-box",
|
|
14
|
+
backgroundImage: "none",
|
|
15
|
+
backgroundOrigin: "padding-box",
|
|
16
|
+
backgroundPosition: "0% 0%",
|
|
17
|
+
backgroundRepeat: "repeat",
|
|
18
|
+
backgroundSize: "auto",
|
|
19
|
+
"border-block-end-color": "#5c1906",
|
|
20
|
+
"border-block-end-style": "solid",
|
|
21
|
+
"border-block-end-width": 2,
|
|
22
|
+
boxShadow: "0 3px 12px rgb(36 20 0 / 0.35)",
|
|
23
|
+
color: "#241400",
|
|
24
|
+
display: "flex",
|
|
25
|
+
flexWrap: "wrap",
|
|
26
|
+
fontFamily: "var(--font-text, system-ui, sans-serif)",
|
|
27
|
+
fontSize: "var(--text-label, 0.875rem)",
|
|
28
|
+
gap: "var(--space-1, 0.25rem) var(--space-3, 0.75rem)",
|
|
29
|
+
"inset-block-start": 0,
|
|
30
|
+
justifyContent: "center",
|
|
31
|
+
lineHeight: 1.35,
|
|
32
|
+
minHeight: "3rem",
|
|
33
|
+
paddingBlock: "max(var(--space-2, 0.5rem), env(safe-area-inset-top))",
|
|
34
|
+
paddingInline: "max(var(--space-4, 1rem), env(safe-area-inset-left)) max(var(--space-4, 1rem), env(safe-area-inset-right))",
|
|
35
|
+
position: "sticky",
|
|
36
|
+
textAlign: "center",
|
|
37
|
+
width: "100%",
|
|
38
|
+
zIndex: "calc(var(--z-tooltip, 3000) + 1)",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as stylex from "@stylexjs/stylex";
|
|
4
|
+
|
|
5
|
+
import { productionDataPreviewNoticeStyles } from "./production-data-preview-notice.stylex.js";
|
|
6
|
+
|
|
7
|
+
export interface ProductionDataPreviewNoticeProps {
|
|
8
|
+
/** A non-empty deployment origin enables the warning without exposing it in the rendered page. */
|
|
9
|
+
readonly surfaceOrigin?: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** A sticky warning for previews that are connected to production data. */
|
|
13
|
+
export function ProductionDataPreviewNotice({
|
|
14
|
+
surfaceOrigin,
|
|
15
|
+
}: ProductionDataPreviewNoticeProps) {
|
|
16
|
+
if (surfaceOrigin === undefined || surfaceOrigin === "") return null;
|
|
17
|
+
|
|
18
|
+
const noticePresentation = stylex.props(productionDataPreviewNoticeStyles.root);
|
|
19
|
+
const emphasisPresentation = stylex.props(
|
|
20
|
+
productionDataPreviewNoticeStyles.emphasis,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<aside
|
|
25
|
+
{...noticePresentation}
|
|
26
|
+
aria-label="Production data preview warning"
|
|
27
|
+
className={`hraness-design-production-data-preview-notice ${noticePresentation.className}`}
|
|
28
|
+
role="alert"
|
|
29
|
+
>
|
|
30
|
+
<strong {...emphasisPresentation}>Production data preview</strong>
|
|
31
|
+
<span>This preview uses production data. Actions are real and affect production.</span>
|
|
32
|
+
</aside>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as stylex from "@stylexjs/stylex";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ProviderMark: a soft accent-tinted tile carrying either vendor-colored art
|
|
5
|
+
* or a retinted monochrome glyph. `--_mark-accent` is set per mark by the
|
|
6
|
+
* component; every color below derives from it so surfaces stay in the
|
|
7
|
+
* mark's palette without per-brand rules. Color art overlays the
|
|
8
|
+
* currentColor glyph, so in forced colors the art hides and the glyph keeps
|
|
9
|
+
* a system color. Marks with no artwork fall back to their monogram.
|
|
10
|
+
*/
|
|
11
|
+
export const providerMarkStyles = stylex.create({
|
|
12
|
+
tile: {
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
aspectRatio: "1",
|
|
15
|
+
backgroundColor: {
|
|
16
|
+
default: "color-mix(in srgb, var(--_mark-accent) 14%, var(--surface, Canvas))",
|
|
17
|
+
"@media (forced-colors: active)": "Canvas",
|
|
18
|
+
},
|
|
19
|
+
backgroundImage: {
|
|
20
|
+
default:
|
|
21
|
+
"linear-gradient(180deg, color-mix(in srgb, white 24%, transparent), transparent 48%), linear-gradient(160deg, color-mix(in srgb, var(--_mark-accent) 26%, transparent), color-mix(in srgb, var(--_mark-accent) 6%, transparent) 74%)",
|
|
22
|
+
"@media (forced-colors: active)": "none",
|
|
23
|
+
},
|
|
24
|
+
borderRadius: "calc(var(--_mark-size) * 0.26)",
|
|
25
|
+
boxShadow: {
|
|
26
|
+
default:
|
|
27
|
+
"inset 0 1px 0 color-mix(in srgb, white 55%, transparent), inset 0 -1px 0 color-mix(in srgb, var(--_mark-accent) 18%, transparent), 0 1px 2px color-mix(in srgb, var(--_mark-accent) 30%, transparent)",
|
|
28
|
+
"@media (forced-colors: active)": "none",
|
|
29
|
+
},
|
|
30
|
+
boxSizing: "border-box",
|
|
31
|
+
color: "light-dark(color-mix(in srgb, var(--_mark-accent) 78%, black), color-mix(in srgb, var(--_mark-accent) 55%, white))",
|
|
32
|
+
display: "inline-flex",
|
|
33
|
+
flexShrink: 0,
|
|
34
|
+
inlineSize: "var(--_mark-size)",
|
|
35
|
+
justifyContent: "center",
|
|
36
|
+
outline: {
|
|
37
|
+
default: "1px solid color-mix(in srgb, var(--_mark-accent) 28%, transparent)",
|
|
38
|
+
"@media (forced-colors: active)": "1px solid ButtonBorder",
|
|
39
|
+
},
|
|
40
|
+
outlineOffset: "-1px",
|
|
41
|
+
position: "relative",
|
|
42
|
+
verticalAlign: "middle",
|
|
43
|
+
},
|
|
44
|
+
solid: {
|
|
45
|
+
backgroundColor: {
|
|
46
|
+
default: "var(--_mark-accent)",
|
|
47
|
+
"@media (forced-colors: active)": "Canvas",
|
|
48
|
+
},
|
|
49
|
+
backgroundImage: {
|
|
50
|
+
default:
|
|
51
|
+
"linear-gradient(180deg, color-mix(in srgb, white 22%, transparent), transparent 52%), linear-gradient(160deg, transparent, color-mix(in srgb, var(--_mark-accent) 78%, black))",
|
|
52
|
+
"@media (forced-colors: active)": "none",
|
|
53
|
+
},
|
|
54
|
+
color: "var(--_mark-on-accent, #f7f6f2)",
|
|
55
|
+
outline: {
|
|
56
|
+
default: "1px solid color-mix(in srgb, var(--_mark-accent) 70%, black)",
|
|
57
|
+
"@media (forced-colors: active)": "1px solid ButtonBorder",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
plain: {
|
|
61
|
+
backgroundColor: "transparent",
|
|
62
|
+
backgroundImage: "none",
|
|
63
|
+
boxShadow: "none",
|
|
64
|
+
color: "light-dark(color-mix(in srgb, var(--_mark-accent) 82%, black), color-mix(in srgb, var(--_mark-accent) 58%, white))",
|
|
65
|
+
outline: "none",
|
|
66
|
+
},
|
|
67
|
+
glyph: {
|
|
68
|
+
blockSize: "64%",
|
|
69
|
+
display: "inline-flex",
|
|
70
|
+
inlineSize: "64%",
|
|
71
|
+
},
|
|
72
|
+
art: {
|
|
73
|
+
blockSize: "68%",
|
|
74
|
+
display: {
|
|
75
|
+
default: "inline-flex",
|
|
76
|
+
"@media (forced-colors: active)": "none",
|
|
77
|
+
},
|
|
78
|
+
inlineSize: "68%",
|
|
79
|
+
inset: "0",
|
|
80
|
+
margin: "auto",
|
|
81
|
+
position: "absolute",
|
|
82
|
+
},
|
|
83
|
+
monogram: {
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
color: "inherit",
|
|
86
|
+
display: {
|
|
87
|
+
default: "none",
|
|
88
|
+
"@media (forced-colors: active)": "inline-flex",
|
|
89
|
+
},
|
|
90
|
+
fontSize: "calc(var(--_mark-size) * 0.36)",
|
|
91
|
+
fontWeight: 700,
|
|
92
|
+
inset: "0",
|
|
93
|
+
justifyContent: "center",
|
|
94
|
+
letterSpacing: "0.02em",
|
|
95
|
+
lineHeight: 1,
|
|
96
|
+
position: "absolute",
|
|
97
|
+
},
|
|
98
|
+
monogramOnly: {
|
|
99
|
+
display: "inline-flex",
|
|
100
|
+
position: "static",
|
|
101
|
+
},
|
|
102
|
+
chip: {
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
display: "inline-flex",
|
|
105
|
+
gap: "calc(var(--_mark-size) * 0.4)",
|
|
106
|
+
minInlineSize: 0,
|
|
107
|
+
},
|
|
108
|
+
chipName: {
|
|
109
|
+
color: "var(--foreground, CanvasText)",
|
|
110
|
+
fontSize: "calc(var(--_mark-size) * 0.46)",
|
|
111
|
+
fontWeight: 550,
|
|
112
|
+
letterSpacing: "-0.01em",
|
|
113
|
+
lineHeight: 1.2,
|
|
114
|
+
minInlineSize: 0,
|
|
115
|
+
overflow: "hidden",
|
|
116
|
+
textOverflow: "ellipsis",
|
|
117
|
+
whiteSpace: "nowrap",
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export function providerMarkClassName(
|
|
122
|
+
part: keyof typeof providerMarkStyles,
|
|
123
|
+
caller?: string,
|
|
124
|
+
): string {
|
|
125
|
+
const hook =
|
|
126
|
+
part === "tile"
|
|
127
|
+
? "hraness-provider-mark"
|
|
128
|
+
: `hraness-provider-mark__${part.replaceAll(/[A-Z]/gu, (c) => `-${c.toLowerCase()}`)}`;
|
|
129
|
+
return [hook, stylex.props(providerMarkStyles[part]).className, caller]
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.join(" ");
|
|
132
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
providerMark,
|
|
5
|
+
providerMarkFallback,
|
|
6
|
+
providerMarkOnAccent,
|
|
7
|
+
type ProviderMarkDescriptor,
|
|
8
|
+
} from "../provider-marks.js";
|
|
9
|
+
import { providerMarkClassName } from "./provider-mark.stylex.js";
|
|
10
|
+
|
|
11
|
+
export type ProviderMarkTone = "tile" | "solid" | "plain";
|
|
12
|
+
|
|
13
|
+
export interface ProviderMarkProps {
|
|
14
|
+
readonly mark: ProviderMarkDescriptor | string;
|
|
15
|
+
readonly className?: string;
|
|
16
|
+
/** Decorative by default; pass a label only when no adjacent name exists. */
|
|
17
|
+
readonly label?: string;
|
|
18
|
+
readonly size?: number;
|
|
19
|
+
readonly tone?: ProviderMarkTone;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface MarkStyle extends CSSProperties {
|
|
23
|
+
"--_mark-accent": string;
|
|
24
|
+
"--_mark-on-accent"?: string;
|
|
25
|
+
"--_mark-size": string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveMark(mark: ProviderMarkDescriptor | string): ProviderMarkDescriptor {
|
|
29
|
+
if (typeof mark !== "string") return mark;
|
|
30
|
+
return providerMark(mark) ?? providerMarkFallback(mark);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function Artwork({
|
|
34
|
+
mark,
|
|
35
|
+
tone,
|
|
36
|
+
}: Readonly<{ mark: ProviderMarkDescriptor; tone: ProviderMarkTone }>) {
|
|
37
|
+
// Only the tinted tile carries vendor-colored art; solid and plain tones
|
|
38
|
+
// use the retintable glyph so contrast stays in the surface's control.
|
|
39
|
+
const art = tone === "tile" ? mark.art : null;
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
<svg
|
|
43
|
+
aria-hidden="true"
|
|
44
|
+
className={providerMarkClassName("glyph")}
|
|
45
|
+
dangerouslySetInnerHTML={{ __html: mark.glyph.body }}
|
|
46
|
+
fill="currentColor"
|
|
47
|
+
viewBox={mark.glyph.viewBox}
|
|
48
|
+
/>
|
|
49
|
+
{art === null ? null : (
|
|
50
|
+
<svg
|
|
51
|
+
aria-hidden="true"
|
|
52
|
+
className={providerMarkClassName("art")}
|
|
53
|
+
dangerouslySetInnerHTML={{ __html: art.body }}
|
|
54
|
+
viewBox={art.viewBox}
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* One provider or agent identity: a soft accent-tinted tile by default, a
|
|
63
|
+
* saturated accent tile with `tone="solid"`, or the bare glyph with
|
|
64
|
+
* `tone="plain"`. Unknown identities render a monogram tile so surfaces
|
|
65
|
+
* never imply an official mark that does not exist.
|
|
66
|
+
*/
|
|
67
|
+
export function ProviderMark({ mark, className, label, size = 32, tone = "tile" }: ProviderMarkProps) {
|
|
68
|
+
if (!Number.isFinite(size) || size <= 0) throw new RangeError("ProviderMark size must be positive and finite.");
|
|
69
|
+
const resolved = resolveMark(mark);
|
|
70
|
+
const style: MarkStyle = {
|
|
71
|
+
"--_mark-accent": resolved.accent,
|
|
72
|
+
"--_mark-size": `${size}px`,
|
|
73
|
+
};
|
|
74
|
+
if (tone === "solid") style["--_mark-on-accent"] = providerMarkOnAccent(resolved);
|
|
75
|
+
const hasArtwork = resolved.glyph.body !== "";
|
|
76
|
+
return (
|
|
77
|
+
<span
|
|
78
|
+
aria-hidden={label === undefined ? true : undefined}
|
|
79
|
+
aria-label={label}
|
|
80
|
+
className={[
|
|
81
|
+
providerMarkClassName("tile"),
|
|
82
|
+
tone === "tile" ? "" : providerMarkClassName(tone),
|
|
83
|
+
className,
|
|
84
|
+
]
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join(" ")}
|
|
87
|
+
role={label === undefined ? undefined : "img"}
|
|
88
|
+
style={style}
|
|
89
|
+
>
|
|
90
|
+
{hasArtwork ? <Artwork mark={resolved} tone={tone} /> : null}
|
|
91
|
+
<span
|
|
92
|
+
aria-hidden="true"
|
|
93
|
+
className={[
|
|
94
|
+
providerMarkClassName("monogram"),
|
|
95
|
+
hasArtwork ? "" : providerMarkClassName("monogramOnly"),
|
|
96
|
+
]
|
|
97
|
+
.filter(Boolean)
|
|
98
|
+
.join(" ")}
|
|
99
|
+
>
|
|
100
|
+
{resolved.monogram}
|
|
101
|
+
</span>
|
|
102
|
+
</span>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface ProviderMarkChipProps extends Omit<ProviderMarkProps, "label"> {
|
|
107
|
+
/** Visible name; defaults to the registered display name. */
|
|
108
|
+
readonly name?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The mark tile beside its display name; labels itself for a11y. */
|
|
112
|
+
export function ProviderMarkChip({ mark, name, ...rest }: ProviderMarkChipProps) {
|
|
113
|
+
const resolved = resolveMark(mark);
|
|
114
|
+
return (
|
|
115
|
+
<span className={providerMarkClassName("chip")} style={{ "--_mark-size": `${rest.size ?? 32}px` } as CSSProperties}>
|
|
116
|
+
<ProviderMark mark={resolved} {...rest} />
|
|
117
|
+
<span className={providerMarkClassName("chipName")}>{name ?? resolved.name}</span>
|
|
118
|
+
</span>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, type ComponentPropsWithoutRef } from "react";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
formatRelativeTime,
|
|
7
|
+
parseRelativeTimeInput,
|
|
8
|
+
resolveRelativeTime,
|
|
9
|
+
type RelativeTimeInput,
|
|
10
|
+
type RelativeTimeNumeric,
|
|
11
|
+
type RelativeTimeReference,
|
|
12
|
+
} from "../relative-time.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `auto` refreshes about as often as the displayed unit can change. A number
|
|
16
|
+
* is a fixed interval in milliseconds. `off` keeps the first reference.
|
|
17
|
+
*/
|
|
18
|
+
export type RelativeTimeRefresh = "auto" | "off" | number;
|
|
19
|
+
|
|
20
|
+
export interface RelativeTimeProps
|
|
21
|
+
extends Omit<ComponentPropsWithoutRef<"time">, "children" | "dateTime" | "title" | "suppressHydrationWarning"> {
|
|
22
|
+
/** The instant to describe: a Date, epoch milliseconds, or a zoned ISO 8601 string. */
|
|
23
|
+
readonly value: RelativeTimeInput;
|
|
24
|
+
/**
|
|
25
|
+
* The reference for the server render and the first client render. After
|
|
26
|
+
* mount the component follows the client clock unless `refreshInterval` is `off`.
|
|
27
|
+
*/
|
|
28
|
+
readonly now?: RelativeTimeReference | undefined;
|
|
29
|
+
readonly locale?: string | undefined;
|
|
30
|
+
readonly numeric?: RelativeTimeNumeric | undefined;
|
|
31
|
+
/** Defaults to `auto`. A fixed interval must be a whole number from 1000 to 2147483647. */
|
|
32
|
+
readonly refreshInterval?: RelativeTimeRefresh | undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const maximumTimeout = 2_147_483_647;
|
|
36
|
+
const autoDelays = {
|
|
37
|
+
second: 1_000,
|
|
38
|
+
minute: 15_000,
|
|
39
|
+
hour: 60_000,
|
|
40
|
+
day: 3_600_000,
|
|
41
|
+
week: 3_600_000,
|
|
42
|
+
month: 3_600_000,
|
|
43
|
+
year: 3_600_000,
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
function parseRefresh(value: unknown): RelativeTimeRefresh {
|
|
47
|
+
if (value === undefined) return "auto";
|
|
48
|
+
if (value === "auto" || value === "off") return value;
|
|
49
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 1_000 && value <= maximumTimeout) return value;
|
|
50
|
+
throw new RangeError(
|
|
51
|
+
`RelativeTime refreshInterval must be "auto", "off", or whole milliseconds from 1000 to ${maximumTimeout}.`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function absoluteFormatter(locale: string | undefined): Intl.DateTimeFormat {
|
|
56
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "long" });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A `<time>` element whose text is relative ("5 minutes ago") and whose
|
|
61
|
+
* `title` is the absolute local date and time. The server render and the
|
|
62
|
+
* hydration render use `now`; the element is then re-created with the client
|
|
63
|
+
* clock and time zone, so no stale server text survives hydration.
|
|
64
|
+
*/
|
|
65
|
+
export function RelativeTime({
|
|
66
|
+
locale,
|
|
67
|
+
now,
|
|
68
|
+
numeric,
|
|
69
|
+
refreshInterval,
|
|
70
|
+
value,
|
|
71
|
+
...timeProps
|
|
72
|
+
}: RelativeTimeProps) {
|
|
73
|
+
const instant = parseRelativeTimeInput(value, "RelativeTime value");
|
|
74
|
+
const refresh = parseRefresh(refreshInterval);
|
|
75
|
+
const time = instant.getTime();
|
|
76
|
+
const [clock, setClock] = useState<number | null>(null);
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
setClock(Date.now());
|
|
80
|
+
if (refresh === "off") return undefined;
|
|
81
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
82
|
+
const schedule = () => {
|
|
83
|
+
const delay = typeof refresh === "number"
|
|
84
|
+
? refresh
|
|
85
|
+
: autoDelays[resolveRelativeTime(time - Date.now()).unit];
|
|
86
|
+
timer = setTimeout(() => {
|
|
87
|
+
setClock(Date.now());
|
|
88
|
+
schedule();
|
|
89
|
+
}, delay);
|
|
90
|
+
};
|
|
91
|
+
schedule();
|
|
92
|
+
return () => clearTimeout(timer);
|
|
93
|
+
}, [refresh, time]);
|
|
94
|
+
|
|
95
|
+
const mounted = clock !== null;
|
|
96
|
+
const reference = refresh === "off" && now !== undefined
|
|
97
|
+
? now
|
|
98
|
+
// Without `now` the first render reads the clock; the mount update replaces it.
|
|
99
|
+
: clock ?? now ?? Date.now();
|
|
100
|
+
const text = formatRelativeTime(instant, {
|
|
101
|
+
now: reference,
|
|
102
|
+
...(locale === undefined ? {} : { locale }),
|
|
103
|
+
...(numeric === undefined ? {} : { numeric }),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<time
|
|
108
|
+
{...timeProps}
|
|
109
|
+
dateTime={instant.toISOString()}
|
|
110
|
+
key={mounted ? "client" : "server"}
|
|
111
|
+
suppressHydrationWarning
|
|
112
|
+
title={absoluteFormatter(locale).format(instant)}
|
|
113
|
+
>
|
|
114
|
+
{text}
|
|
115
|
+
</time>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as stylex from "@stylexjs/stylex";
|
|
2
|
+
|
|
3
|
+
export const routeStateStyles = stylex.create({
|
|
4
|
+
content: {
|
|
5
|
+
display: "grid",
|
|
6
|
+
gridRow: 2,
|
|
7
|
+
"min-block-size": 0,
|
|
8
|
+
placeItems: "center",
|
|
9
|
+
},
|
|
10
|
+
header: {
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
alignSelf: "start",
|
|
13
|
+
display: "flex",
|
|
14
|
+
justifyContent: "flex-end",
|
|
15
|
+
justifySelf: "stretch",
|
|
16
|
+
"min-inline-size": 0,
|
|
17
|
+
padding: "var(--layout-chrome-inset)",
|
|
18
|
+
},
|
|
19
|
+
loading: {
|
|
20
|
+
display: "grid",
|
|
21
|
+
gap: "var(--space-4)",
|
|
22
|
+
"inline-size": "min(100%, 36rem)",
|
|
23
|
+
},
|
|
24
|
+
root: {
|
|
25
|
+
display: "grid",
|
|
26
|
+
gridTemplateRows: "auto minmax(0, 1fr)",
|
|
27
|
+
"min-block-size": "100%",
|
|
28
|
+
},
|
|
29
|
+
row: {
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
display: "flex",
|
|
32
|
+
flexWrap: "wrap",
|
|
33
|
+
gap: "var(--space-2)",
|
|
34
|
+
},
|
|
35
|
+
skeletons: {
|
|
36
|
+
display: "grid",
|
|
37
|
+
gap: "var(--space-2)",
|
|
38
|
+
},
|
|
39
|
+
});
|