@krizaka/ui 3.10.0 → 3.11.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/blocks.css +9 -2
- package/dist/link-card.d.ts +7 -1
- package/dist/link-card.js +8 -5
- package/dist/link-card.js.map +1 -1
- package/package.json +6 -6
- package/registry/card-grid.json +10 -1
- package/registry/examples/card-grid/aligned.d.ts +4 -0
- package/registry/examples/card-grid/aligned.js +18 -0
- package/registry/examples/card-grid/aligned.tsx +16 -0
- package/registry/index.json +5 -5
- package/registry/link-card.json +17 -2
- package/registry/map.json +1 -1
- package/registry/nav-bar.json +1 -1
- package/registry/site-footer.json +1 -1
package/dist/blocks.css
CHANGED
|
@@ -247,11 +247,18 @@ a.kz-fact:focus-visible { outline: 2px solid var(--kz-ring, var(--kz-accent)); o
|
|
|
247
247
|
text-decoration: none;
|
|
248
248
|
color: var(--kz-text-primary);
|
|
249
249
|
}
|
|
250
|
-
.kz-link-card[data-layout="stack"] {
|
|
250
|
+
.kz-link-card[data-layout="stack"] { display: flex; flex-direction: column; align-items: stretch; gap: 12px; }
|
|
251
|
+
.kz-link-card[data-layout="stack"] > .kz-block-icon { align-self: flex-start; }
|
|
252
|
+
.kz-link-card[data-layout="stack"] > .kz-link-card-body { flex: 1; }
|
|
251
253
|
.kz-link-card:focus-visible { outline: 2px solid var(--kz-ring, var(--kz-accent)); outline-offset: 2px; }
|
|
252
254
|
.kz-link-card > .kz-block-icon { grid-row: span 3; }
|
|
253
255
|
.kz-link-card[data-layout="stack"] > .kz-block-icon { grid-row: auto; }
|
|
254
|
-
.kz-link-card-body { display:
|
|
256
|
+
.kz-link-card-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; align-self: stretch; }
|
|
257
|
+
.kz-link-card:not([data-layout="stack"]) > .kz-block-icon + .kz-link-card-body { grid-column: 2; grid-row: 1 / span 3; }
|
|
258
|
+
/* The foot (facts + the way in) is pinned to the bottom of a card taller than its text, so a row of cards at one height
|
|
259
|
+
keeps its status and its link on one line. `data-footer="top"` keeps them under the text. */
|
|
260
|
+
.kz-link-card-foot { display: grid; gap: 5px; margin-top: auto; }
|
|
261
|
+
.kz-link-card[data-footer="top"] .kz-link-card-foot { margin-top: 0; }
|
|
255
262
|
.kz-link-card-meta { margin: 0; font-family: var(--kz-font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--kz-hue-text, var(--kz-accent-text)); }
|
|
256
263
|
.kz-link-card-title { margin: 0; font-family: var(--kz-font-display); font-size: 16.5px; font-weight: 750; letter-spacing: -0.012em; line-height: 1.3; color: var(--kz-text-primary); }
|
|
257
264
|
.kz-link-card-text { margin: 0; font-size: 14px; line-height: 1.55; color: var(--kz-text-secondary); }
|
package/dist/link-card.d.ts
CHANGED
|
@@ -26,10 +26,16 @@ type LinkCardProps = Omit<React.ComponentProps<"a">, "title" | "href"> & {
|
|
|
26
26
|
* @default "row"
|
|
27
27
|
*/
|
|
28
28
|
layout?: "row" | "stack";
|
|
29
|
+
/**
|
|
30
|
+
* Where the facts (`extra`) and the way in (`cta`) sit when the card is taller than its text — a row of cards at one
|
|
31
|
+
* height: `bottom` pins them to the card's foot, so they share a line across the row; `top` leaves them under the text.
|
|
32
|
+
* @default "bottom"
|
|
33
|
+
*/
|
|
34
|
+
footerAlign?: "bottom" | "top";
|
|
29
35
|
/** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */
|
|
30
36
|
linkAs?: LinkComponent;
|
|
31
37
|
};
|
|
32
38
|
/** An entry point: the whole card is one link — icon, title, sentence, the way in. Lifts and lights under the pointer. */
|
|
33
|
-
declare function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title, layout, linkAs, className, ...props }: LinkCardProps): React.JSX.Element;
|
|
39
|
+
declare function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title, layout, footerAlign, linkAs, className, ...props }: LinkCardProps): React.JSX.Element;
|
|
34
40
|
|
|
35
41
|
export { LinkCard, type LinkCardProps, LinkComponent };
|
package/dist/link-card.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
|
|
13
13
|
// src/link-card/link-card.tsx
|
|
14
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
-
function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title = "h3", layout = "row", linkAs, className, ...props }) {
|
|
15
|
+
function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title = "h3", layout = "row", footerAlign = "bottom", linkAs, className, ...props }) {
|
|
16
16
|
const external = isExternal(href);
|
|
17
17
|
return /* @__PURE__ */ jsxs(
|
|
18
18
|
BlockLink,
|
|
@@ -23,6 +23,7 @@ function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: T
|
|
|
23
23
|
className: cn("kz-block-card kz-link-card", className),
|
|
24
24
|
"data-kz-light": "",
|
|
25
25
|
"data-layout": layout === "row" ? void 0 : layout,
|
|
26
|
+
"data-footer": footerAlign === "top" ? "top" : void 0,
|
|
26
27
|
...props,
|
|
27
28
|
children: [
|
|
28
29
|
icon && /* @__PURE__ */ jsx("span", { className: "kz-block-icon", "aria-hidden": true, children: icon }),
|
|
@@ -30,10 +31,12 @@ function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: T
|
|
|
30
31
|
meta && /* @__PURE__ */ jsx("p", { className: "kz-link-card-meta", children: meta }),
|
|
31
32
|
/* @__PURE__ */ jsx(Title, { className: "kz-link-card-title", children: title }),
|
|
32
33
|
description && /* @__PURE__ */ jsx("p", { className: "kz-link-card-text", children: description }),
|
|
33
|
-
extra && /* @__PURE__ */
|
|
34
|
-
|
|
35
|
-
cta,
|
|
36
|
-
|
|
34
|
+
(extra || cta || external) && /* @__PURE__ */ jsxs("div", { className: "kz-link-card-foot", children: [
|
|
35
|
+
extra && /* @__PURE__ */ jsx("div", { className: "kz-link-card-extra", children: extra }),
|
|
36
|
+
(cta || external) && /* @__PURE__ */ jsxs("span", { className: "kz-link-card-cta", children: [
|
|
37
|
+
cta,
|
|
38
|
+
external ? /* @__PURE__ */ jsx(ExternalGlyph, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ArrowGlyph, { className: "h-3.5 w-3.5" })
|
|
39
|
+
] })
|
|
37
40
|
] })
|
|
38
41
|
] })
|
|
39
42
|
]
|
package/dist/link-card.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/link-card/link-card.tsx"],"sourcesContent":["// Server-safe. A card that leads somewhere (blocks.css): the whole card is the link — an icon, a title, one sentence,\n// the way in. Compact, one look for every entry point of a page (a documentation hub, the products, the next step).\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { ArrowGlyph, ExternalGlyph } from \"../glyphs\";\n\nexport type LinkCardProps = Omit<React.ComponentProps<\"a\">, \"title\" | \"href\"> & {\n /** Where the card leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** The title: the name of the destination. */\n title: React.ReactNode;\n /** One sentence: what the reader finds there. */\n description?: React.ReactNode;\n /** The icon tile on the left (a product mark, a signature icon): decorative, the title names the link. */\n icon?: React.ReactNode;\n /** A short mono line above the title (a category, a version). */\n meta?: React.ReactNode;\n /** The way in, under the text, with an arrow: `Open`, `Read the guide`. */\n cta?: React.ReactNode;\n /** Under the text, before the way in: facts (`Facts`), badges. Never another link — the card is one. */\n extra?: React.ReactNode;\n /**\n * The title's element: a heading when the cards are the content of a section (a hub), `span` inside a list of links.\n * @default \"h3\"\n */\n titleAs?: \"h2\" | \"h3\" | \"h4\" | \"span\";\n /**\n * `row`: the icon beside the text (compact, the default); `stack`: the icon above it.\n * @default \"row\"\n */\n layout?: \"row\" | \"stack\";\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** An entry point: the whole card is one link — icon, title, sentence, the way in. Lifts and lights under the pointer. */\nexport function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title = \"h3\", layout = \"row\", linkAs, className, ...props }: LinkCardProps) {\n const external = isExternal(href);\n return (\n <BlockLink\n href={href}\n linkAs={linkAs}\n external={external}\n className={cn(\"kz-block-card kz-link-card\", className)}\n data-kz-light=\"\"\n data-layout={layout === \"row\" ? undefined : layout}\n {...props}\n >\n {icon && (\n <span className=\"kz-block-icon\" aria-hidden>\n {icon}\n </span>\n )}\n <div className=\"kz-link-card-body\">\n {meta && <p className=\"kz-link-card-meta\">{meta}</p>}\n <Title className=\"kz-link-card-title\">{title}</Title>\n {description && <p className=\"kz-link-card-text\">{description}</p>}\n {extra && <div className=\"kz-link-card-extra\">{extra}</div>}\n
|
|
1
|
+
{"version":3,"sources":["../src/link-card/link-card.tsx"],"sourcesContent":["// Server-safe. A card that leads somewhere (blocks.css): the whole card is the link — an icon, a title, one sentence,\n// the way in. Compact, one look for every entry point of a page (a documentation hub, the products, the next step).\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { ArrowGlyph, ExternalGlyph } from \"../glyphs\";\n\nexport type LinkCardProps = Omit<React.ComponentProps<\"a\">, \"title\" | \"href\"> & {\n /** Where the card leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** The title: the name of the destination. */\n title: React.ReactNode;\n /** One sentence: what the reader finds there. */\n description?: React.ReactNode;\n /** The icon tile on the left (a product mark, a signature icon): decorative, the title names the link. */\n icon?: React.ReactNode;\n /** A short mono line above the title (a category, a version). */\n meta?: React.ReactNode;\n /** The way in, under the text, with an arrow: `Open`, `Read the guide`. */\n cta?: React.ReactNode;\n /** Under the text, before the way in: facts (`Facts`), badges. Never another link — the card is one. */\n extra?: React.ReactNode;\n /**\n * The title's element: a heading when the cards are the content of a section (a hub), `span` inside a list of links.\n * @default \"h3\"\n */\n titleAs?: \"h2\" | \"h3\" | \"h4\" | \"span\";\n /**\n * `row`: the icon beside the text (compact, the default); `stack`: the icon above it.\n * @default \"row\"\n */\n layout?: \"row\" | \"stack\";\n /**\n * Where the facts (`extra`) and the way in (`cta`) sit when the card is taller than its text — a row of cards at one\n * height: `bottom` pins them to the card's foot, so they share a line across the row; `top` leaves them under the text.\n * @default \"bottom\"\n */\n footerAlign?: \"bottom\" | \"top\";\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** An entry point: the whole card is one link — icon, title, sentence, the way in. Lifts and lights under the pointer. */\nexport function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title = \"h3\", layout = \"row\", footerAlign = \"bottom\", linkAs, className, ...props }: LinkCardProps) {\n const external = isExternal(href);\n return (\n <BlockLink\n href={href}\n linkAs={linkAs}\n external={external}\n className={cn(\"kz-block-card kz-link-card\", className)}\n data-kz-light=\"\"\n data-layout={layout === \"row\" ? undefined : layout}\n data-footer={footerAlign === \"top\" ? \"top\" : undefined}\n {...props}\n >\n {icon && (\n <span className=\"kz-block-icon\" aria-hidden>\n {icon}\n </span>\n )}\n <div className=\"kz-link-card-body\">\n {meta && <p className=\"kz-link-card-meta\">{meta}</p>}\n <Title className=\"kz-link-card-title\">{title}</Title>\n {description && <p className=\"kz-link-card-text\">{description}</p>}\n {(extra || cta || external) && (\n <div className=\"kz-link-card-foot\">\n {extra && <div className=\"kz-link-card-extra\">{extra}</div>}\n {(cta || external) && (\n <span className=\"kz-link-card-cta\">\n {cta}\n {external ? <ExternalGlyph className=\"h-3.5 w-3.5\" /> : <ArrowGlyph className=\"h-3.5 w-3.5\" />}\n </span>\n )}\n </div>\n )}\n </div>\n </BlockLink>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA0DQ,cAYM,YAZN;AAdD,SAAS,SAAS,EAAE,MAAM,OAAO,aAAa,MAAM,MAAM,KAAK,OAAO,SAAS,QAAQ,MAAM,SAAS,OAAO,cAAc,UAAU,QAAQ,WAAW,GAAG,MAAM,GAAkB;AACxL,QAAM,WAAW,WAAW,IAAI;AAChC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,GAAG,8BAA8B,SAAS;AAAA,MACrD,iBAAc;AAAA,MACd,eAAa,WAAW,QAAQ,SAAY;AAAA,MAC5C,eAAa,gBAAgB,QAAQ,QAAQ;AAAA,MAC5C,GAAG;AAAA,MAEH;AAAA,gBACC,oBAAC,UAAK,WAAU,iBAAgB,eAAW,MACxC,gBACH;AAAA,QAEF,qBAAC,SAAI,WAAU,qBACZ;AAAA,kBAAQ,oBAAC,OAAE,WAAU,qBAAqB,gBAAK;AAAA,UAChD,oBAAC,SAAM,WAAU,sBAAsB,iBAAM;AAAA,UAC5C,eAAe,oBAAC,OAAE,WAAU,qBAAqB,uBAAY;AAAA,WAC5D,SAAS,OAAO,aAChB,qBAAC,SAAI,WAAU,qBACZ;AAAA,qBAAS,oBAAC,SAAI,WAAU,sBAAsB,iBAAM;AAAA,aACnD,OAAO,aACP,qBAAC,UAAK,WAAU,oBACb;AAAA;AAAA,cACA,WAAW,oBAAC,iBAAc,WAAU,eAAc,IAAK,oBAAC,cAAW,WAAU,eAAc;AAAA,eAC9F;AAAA,aAEJ;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia, Yasakia, Yororis) and the motion signature — web and /native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"krizaka",
|
|
@@ -614,7 +614,7 @@
|
|
|
614
614
|
],
|
|
615
615
|
"peerDependencies": {
|
|
616
616
|
"@krizaka/query": ">=0.2.1",
|
|
617
|
-
"@krizaka/tailwind": ">=3.
|
|
617
|
+
"@krizaka/tailwind": ">=3.11.0",
|
|
618
618
|
"@maplibre/maplibre-react-native": ">=11",
|
|
619
619
|
"@shopify/flash-list": ">=2",
|
|
620
620
|
"expo-camera": ">=16",
|
|
@@ -672,8 +672,8 @@
|
|
|
672
672
|
"typescript": "^5.9.0",
|
|
673
673
|
"vitest": "^4.1.11",
|
|
674
674
|
"zod": "^4.6.5",
|
|
675
|
-
"@krizaka/
|
|
676
|
-
"@krizaka/
|
|
675
|
+
"@krizaka/query": "0.2.1",
|
|
676
|
+
"@krizaka/config": "0.4.1"
|
|
677
677
|
},
|
|
678
678
|
"engines": {
|
|
679
679
|
"node": ">=22"
|
|
@@ -2098,8 +2098,8 @@
|
|
|
2098
2098
|
"sonner": "^2.0.8",
|
|
2099
2099
|
"tailwind-variants": "^3.3.1",
|
|
2100
2100
|
"@krizaka/intl": "^0.2.1",
|
|
2101
|
-
"@krizaka/
|
|
2102
|
-
"@krizaka/
|
|
2101
|
+
"@krizaka/tokens": "^3.11.0",
|
|
2102
|
+
"@krizaka/pacer": "^0.1.0"
|
|
2103
2103
|
},
|
|
2104
2104
|
"scripts": {
|
|
2105
2105
|
"build": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});for(const f of fs.readdirSync('registry/examples',{recursive:true}))if(/\\.(js|d\\.ts)$/.test(f))fs.rmSync('registry/examples/'+f)\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css');require('node:fs').copyFileSync('src/blocks.css','dist/blocks.css');require('node:fs').copyFileSync('src/story.css','dist/story.css')\" && node scripts/build-registry.mjs",
|
package/registry/card-grid.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"Set `min` to the narrowest a card still reads well; the grid does the rest at every width.",
|
|
27
27
|
"`fit=\"fit\"` for two or three cards that should share the whole width; `fill` keeps a lone card at its size.",
|
|
28
28
|
"Keep `reveal` on below the first screen, off on it; `columns` makes the stagger restart on each row.",
|
|
29
|
+
"The default `align=\"stretch\"` gives a row one height; the cards pin their foot to the bottom (`LinkCard` facts + way in, `ArgumentCard` actions) so the footers line up. `align=\"start\"` for cards that unfold.",
|
|
29
30
|
"`as=\"ul\"` when the cards are a list of links."
|
|
30
31
|
],
|
|
31
32
|
"accessibility": {
|
|
@@ -117,6 +118,14 @@
|
|
|
117
118
|
"module": "@krizaka/ui/registry/examples/card-grid/products",
|
|
118
119
|
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { CardGrid } from \"@krizaka/ui/card-grid\";\nimport { LinkCard } from \"@krizaka/ui/link-card\";\n\n// `fit=\"fit\"`: the cards of a short row share the whole width — two products, two halves. One column on a phone.\nexport default function CardGridProducts() {\n return (\n <CardGrid className=\"w-full max-w-3xl\" fit=\"fit\" reveal={false}>\n <LinkCard href=\"#orazaka\" icon={<ProductLogo id=\"orazaka\" size={24} animated={false} />} title=\"Orazaka\" description=\"Sovereign AI on your own servers.\" cta=\"Overview\" />\n <LinkCard href=\"#orochia\" icon={<ProductLogo id=\"orochia\" size={24} animated={false} />} title=\"Orochia\" description=\"Creators paid for what they make.\" cta=\"Overview\" />\n </CardGrid>\n );\n}\n"
|
|
119
120
|
},
|
|
121
|
+
{
|
|
122
|
+
"name": "aligned",
|
|
123
|
+
"title": "An aligned row",
|
|
124
|
+
"description": "Cards with sentences of different lengths: one height, and the facts and the way in of every card on the same line.",
|
|
125
|
+
"path": "examples/card-grid/aligned.tsx",
|
|
126
|
+
"module": "@krizaka/ui/registry/examples/card-grid/aligned",
|
|
127
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { CardGrid } from \"@krizaka/ui/card-grid\";\nimport { LinkCard } from \"@krizaka/ui/link-card\";\nimport { Facts } from \"@krizaka/ui/page\";\n\n// A row of cards whose sentences have different lengths: the cells share one height and each card pins its facts\n// and its way in to the bottom, so the status and the link of every card sit on the same line.\nexport default function CardGridAligned() {\n return (\n <CardGrid className=\"w-full max-w-4xl\" min={220} columns={3} reveal={false}>\n <LinkCard layout=\"stack\" href=\"#orazaka\" icon={<ProductLogo id=\"orazaka\" size={28} animated={false} />} title=\"Orazaka\" description=\"Sovereign AI on your own servers: chat, image, video and voice.\" extra={<Facts items={[{ label: \"Dev preview\", tone: \"ok\" }]} />} cta=\"Open orazaka\" />\n <LinkCard layout=\"stack\" href=\"#orochia\" icon={<ProductLogo id=\"orochia\" size={28} animated={false} />} title=\"Orochia\" description=\"Creators, paid.\" extra={<Facts items={[{ label: \"Coming soon\", tone: \"wip\" }]} />} cta=\"Open orochia\" />\n <LinkCard layout=\"stack\" href=\"#yororis\" icon={<ProductLogo id=\"yororis\" size={28} animated={false} />} title=\"Yororis\" description=\"A quiet place to keep what matters to you, built around the moments of a day and the threads between them.\" extra={<Facts items={[{ label: \"Under construction\", tone: \"wip\" }]} />} cta=\"Open yororis\" />\n </CardGrid>\n );\n}\n"
|
|
128
|
+
},
|
|
120
129
|
{
|
|
121
130
|
"name": "reveal",
|
|
122
131
|
"title": "A list of sections",
|
|
@@ -139,7 +148,7 @@
|
|
|
139
148
|
},
|
|
140
149
|
{
|
|
141
150
|
"path": "card-grid/meta.ts",
|
|
142
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Card grid\",\n summary:\n \"A responsive grid of cards: as many columns as the width allows (one on a phone), the cells of a row at one height, a staggered reveal and the light that follows the pointer.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n whenToUse: [\n \"Entry points of a hub (`LinkCard`), the reasons of a product page (`ArgumentCard`), plans, products.\",\n \"Any set of peer cards that should read as one group with one rhythm.\",\n ],\n whenNotToUse: [\n { when: \"For a sequence the reader follows in order.\", use: \"stepper\" },\n { when: \"For rows of data to compare or sort.\", use: \"data-table\" },\n ],\n bestPractices: [\n \"Set `min` to the narrowest a card still reads well; the grid does the rest at every width.\",\n \"`fit=\\\"fit\\\"` for two or three cards that should share the whole width; `fill` keeps a lone card at its size.\",\n \"Keep `reveal` on below the first screen, off on it; `columns` makes the stagger restart on each row.\",\n \"`as=\\\"ul\\\"` when the cards are a list of links.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"The grid adds no role; `as=\\\"ul\\\"` makes it a list (each cell an `li`).\",\n \"The pointer's light is decorative and never runs on touch screens or under `prefers-reduced-motion`.\",\n \"Revealed cells are only hidden visually until they enter the viewport; under reduced motion they never move.\",\n ],\n },\n related: [\"link-card\", \"argument-card\", \"pointer-light\"],\n web: {\n imports: [\"CardGrid\"],\n examples: [\n { name: \"products\", title: \"Two halves\", description: \"`fit=\\\"fit\\\"`: two cards share the whole width.\" },\n { name: \"reveal\", title: \"A list of sections\", description: \"`as=\\\"ul\\\"`, six links that rise staggered by column on a real page.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
151
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Card grid\",\n summary:\n \"A responsive grid of cards: as many columns as the width allows (one on a phone), the cells of a row at one height, a staggered reveal and the light that follows the pointer.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n whenToUse: [\n \"Entry points of a hub (`LinkCard`), the reasons of a product page (`ArgumentCard`), plans, products.\",\n \"Any set of peer cards that should read as one group with one rhythm.\",\n ],\n whenNotToUse: [\n { when: \"For a sequence the reader follows in order.\", use: \"stepper\" },\n { when: \"For rows of data to compare or sort.\", use: \"data-table\" },\n ],\n bestPractices: [\n \"Set `min` to the narrowest a card still reads well; the grid does the rest at every width.\",\n \"`fit=\\\"fit\\\"` for two or three cards that should share the whole width; `fill` keeps a lone card at its size.\",\n \"Keep `reveal` on below the first screen, off on it; `columns` makes the stagger restart on each row.\",\n \"The default `align=\\\"stretch\\\"` gives a row one height; the cards pin their foot to the bottom (`LinkCard` facts + way in, `ArgumentCard` actions) so the footers line up. `align=\\\"start\\\"` for cards that unfold.\",\n \"`as=\\\"ul\\\"` when the cards are a list of links.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"The grid adds no role; `as=\\\"ul\\\"` makes it a list (each cell an `li`).\",\n \"The pointer's light is decorative and never runs on touch screens or under `prefers-reduced-motion`.\",\n \"Revealed cells are only hidden visually until they enter the viewport; under reduced motion they never move.\",\n ],\n },\n related: [\"link-card\", \"argument-card\", \"pointer-light\"],\n web: {\n imports: [\"CardGrid\"],\n examples: [\n { name: \"products\", title: \"Two halves\", description: \"`fit=\\\"fit\\\"`: two cards share the whole width.\" },\n { name: \"aligned\", title: \"An aligned row\", description: \"Cards with sentences of different lengths: one height, and the facts and the way in of every card on the same line.\" },\n { name: \"reveal\", title: \"A list of sections\", description: \"`as=\\\"ul\\\"`, six links that rise staggered by column on a real page.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
143
152
|
},
|
|
144
153
|
{
|
|
145
154
|
"path": "meta.ts",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// registry/examples/card-grid/aligned.tsx
|
|
4
|
+
import { ProductLogo } from "@krizaka/ui";
|
|
5
|
+
import { CardGrid } from "@krizaka/ui/card-grid";
|
|
6
|
+
import { LinkCard } from "@krizaka/ui/link-card";
|
|
7
|
+
import { Facts } from "@krizaka/ui/page";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function CardGridAligned() {
|
|
10
|
+
return /* @__PURE__ */ jsxs(CardGrid, { className: "w-full max-w-4xl", min: 220, columns: 3, reveal: false, children: [
|
|
11
|
+
/* @__PURE__ */ jsx(LinkCard, { layout: "stack", href: "#orazaka", icon: /* @__PURE__ */ jsx(ProductLogo, { id: "orazaka", size: 28, animated: false }), title: "Orazaka", description: "Sovereign AI on your own servers: chat, image, video and voice.", extra: /* @__PURE__ */ jsx(Facts, { items: [{ label: "Dev preview", tone: "ok" }] }), cta: "Open orazaka" }),
|
|
12
|
+
/* @__PURE__ */ jsx(LinkCard, { layout: "stack", href: "#orochia", icon: /* @__PURE__ */ jsx(ProductLogo, { id: "orochia", size: 28, animated: false }), title: "Orochia", description: "Creators, paid.", extra: /* @__PURE__ */ jsx(Facts, { items: [{ label: "Coming soon", tone: "wip" }] }), cta: "Open orochia" }),
|
|
13
|
+
/* @__PURE__ */ jsx(LinkCard, { layout: "stack", href: "#yororis", icon: /* @__PURE__ */ jsx(ProductLogo, { id: "yororis", size: 28, animated: false }), title: "Yororis", description: "A quiet place to keep what matters to you, built around the moments of a day and the threads between them.", extra: /* @__PURE__ */ jsx(Facts, { items: [{ label: "Under construction", tone: "wip" }] }), cta: "Open yororis" })
|
|
14
|
+
] });
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
CardGridAligned as default
|
|
18
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ProductLogo } from "@krizaka/ui";
|
|
2
|
+
import { CardGrid } from "@krizaka/ui/card-grid";
|
|
3
|
+
import { LinkCard } from "@krizaka/ui/link-card";
|
|
4
|
+
import { Facts } from "@krizaka/ui/page";
|
|
5
|
+
|
|
6
|
+
// A row of cards whose sentences have different lengths: the cells share one height and each card pins its facts
|
|
7
|
+
// and its way in to the bottom, so the status and the link of every card sit on the same line.
|
|
8
|
+
export default function CardGridAligned() {
|
|
9
|
+
return (
|
|
10
|
+
<CardGrid className="w-full max-w-4xl" min={220} columns={3} reveal={false}>
|
|
11
|
+
<LinkCard layout="stack" href="#orazaka" icon={<ProductLogo id="orazaka" size={28} animated={false} />} title="Orazaka" description="Sovereign AI on your own servers: chat, image, video and voice." extra={<Facts items={[{ label: "Dev preview", tone: "ok" }]} />} cta="Open orazaka" />
|
|
12
|
+
<LinkCard layout="stack" href="#orochia" icon={<ProductLogo id="orochia" size={28} animated={false} />} title="Orochia" description="Creators, paid." extra={<Facts items={[{ label: "Coming soon", tone: "wip" }]} />} cta="Open orochia" />
|
|
13
|
+
<LinkCard layout="stack" href="#yororis" icon={<ProductLogo id="yororis" size={28} animated={false} />} title="Yororis" description="A quiet place to keep what matters to you, built around the moments of a day and the threads between them." extra={<Facts items={[{ label: "Under construction", tone: "wip" }]} />} cta="Open yororis" />
|
|
14
|
+
</CardGrid>
|
|
15
|
+
);
|
|
16
|
+
}
|
package/registry/index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"templatePurposes": {
|
|
5
5
|
"convince": {
|
|
6
6
|
"title": "Convince",
|
|
@@ -683,7 +683,7 @@
|
|
|
683
683
|
"category": "blocks",
|
|
684
684
|
"platforms": "web",
|
|
685
685
|
"examples": {
|
|
686
|
-
"web":
|
|
686
|
+
"web": 3,
|
|
687
687
|
"native": 0
|
|
688
688
|
},
|
|
689
689
|
"dependencies": [],
|
|
@@ -1731,7 +1731,7 @@
|
|
|
1731
1731
|
"native": 2
|
|
1732
1732
|
},
|
|
1733
1733
|
"dependencies": [
|
|
1734
|
-
"@krizaka/tokens@^3.
|
|
1734
|
+
"@krizaka/tokens@^3.11.0",
|
|
1735
1735
|
"@protomaps/basemaps@5.7.2",
|
|
1736
1736
|
"tailwind-variants@^3.3.1"
|
|
1737
1737
|
],
|
|
@@ -1939,7 +1939,7 @@
|
|
|
1939
1939
|
"native": 0
|
|
1940
1940
|
},
|
|
1941
1941
|
"dependencies": [
|
|
1942
|
-
"@krizaka/tokens@^3.
|
|
1942
|
+
"@krizaka/tokens@^3.11.0",
|
|
1943
1943
|
"radix-ui@^1.7.0",
|
|
1944
1944
|
"tailwind-variants@^3.3.1"
|
|
1945
1945
|
],
|
|
@@ -2455,7 +2455,7 @@
|
|
|
2455
2455
|
"native": 0
|
|
2456
2456
|
},
|
|
2457
2457
|
"dependencies": [
|
|
2458
|
-
"@krizaka/tokens@^3.
|
|
2458
|
+
"@krizaka/tokens@^3.11.0",
|
|
2459
2459
|
"radix-ui@^1.7.0",
|
|
2460
2460
|
"tailwind-variants@^3.3.1"
|
|
2461
2461
|
],
|
package/registry/link-card.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"One sentence of description; the title names the destination, the way in (`cta`) says the action.",
|
|
27
27
|
"Use the product's mark or a signature icon as `icon`, never a placeholder.",
|
|
28
28
|
"Pass the app's router link as `linkAs` (Next's `Link`) so internal links prefetch.",
|
|
29
|
+
"In a row of cards at one height (`CardGrid`), the facts (`extra`) and the way in (`cta`) are pinned to the foot of each card, so they share a line whatever the length of the sentences; `footerAlign=\"top\"` keeps them under the text.",
|
|
29
30
|
"In a hub, keep the default `h3` titles under the section's `h2`; in a list of links, `titleAs=\"span\"`."
|
|
30
31
|
],
|
|
31
32
|
"accessibility": {
|
|
@@ -94,6 +95,13 @@
|
|
|
94
95
|
"description": "Under the text, before the way in: facts (`Facts`), badges. Never another link — the card is one.",
|
|
95
96
|
"required": false
|
|
96
97
|
},
|
|
98
|
+
{
|
|
99
|
+
"name": "footerAlign",
|
|
100
|
+
"type": "\"bottom\" | \"top\"",
|
|
101
|
+
"default": "bottom",
|
|
102
|
+
"description": "Where the facts (`extra`) and the way in (`cta`) sit when the card is taller than its text — a row of cards at one\nheight: `bottom` pins them to the card's foot, so they share a line across the row; `top` leaves them under the text.",
|
|
103
|
+
"required": false
|
|
104
|
+
},
|
|
97
105
|
{
|
|
98
106
|
"name": "icon",
|
|
99
107
|
"type": "ReactNode",
|
|
@@ -175,11 +183,11 @@
|
|
|
175
183
|
},
|
|
176
184
|
{
|
|
177
185
|
"path": "link-card/link-card.tsx",
|
|
178
|
-
"content": "// Server-safe. A card that leads somewhere (blocks.css): the whole card is the link — an icon, a title, one sentence,\n// the way in. Compact, one look for every entry point of a page (a documentation hub, the products, the next step).\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { ArrowGlyph, ExternalGlyph } from \"../glyphs\";\n\nexport type LinkCardProps = Omit<React.ComponentProps<\"a\">, \"title\" | \"href\"> & {\n /** Where the card leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** The title: the name of the destination. */\n title: React.ReactNode;\n /** One sentence: what the reader finds there. */\n description?: React.ReactNode;\n /** The icon tile on the left (a product mark, a signature icon): decorative, the title names the link. */\n icon?: React.ReactNode;\n /** A short mono line above the title (a category, a version). */\n meta?: React.ReactNode;\n /** The way in, under the text, with an arrow: `Open`, `Read the guide`. */\n cta?: React.ReactNode;\n /** Under the text, before the way in: facts (`Facts`), badges. Never another link — the card is one. */\n extra?: React.ReactNode;\n /**\n * The title's element: a heading when the cards are the content of a section (a hub), `span` inside a list of links.\n * @default \"h3\"\n */\n titleAs?: \"h2\" | \"h3\" | \"h4\" | \"span\";\n /**\n * `row`: the icon beside the text (compact, the default); `stack`: the icon above it.\n * @default \"row\"\n */\n layout?: \"row\" | \"stack\";\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** An entry point: the whole card is one link — icon, title, sentence, the way in. Lifts and lights under the pointer. */\nexport function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title = \"h3\", layout = \"row\", linkAs, className, ...props }: LinkCardProps) {\n const external = isExternal(href);\n return (\n <BlockLink\n href={href}\n linkAs={linkAs}\n external={external}\n className={cn(\"kz-block-card kz-link-card\", className)}\n data-kz-light=\"\"\n data-layout={layout === \"row\" ? undefined : layout}\n {...props}\n >\n {icon && (\n <span className=\"kz-block-icon\" aria-hidden>\n {icon}\n </span>\n )}\n <div className=\"kz-link-card-body\">\n {meta && <p className=\"kz-link-card-meta\">{meta}</p>}\n <Title className=\"kz-link-card-title\">{title}</Title>\n {description && <p className=\"kz-link-card-text\">{description}</p>}\n {extra && <div className=\"kz-link-card-extra\">{extra}</div>}\n
|
|
186
|
+
"content": "// Server-safe. A card that leads somewhere (blocks.css): the whole card is the link — an icon, a title, one sentence,\n// the way in. Compact, one look for every entry point of a page (a documentation hub, the products, the next step).\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { ArrowGlyph, ExternalGlyph } from \"../glyphs\";\n\nexport type LinkCardProps = Omit<React.ComponentProps<\"a\">, \"title\" | \"href\"> & {\n /** Where the card leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** The title: the name of the destination. */\n title: React.ReactNode;\n /** One sentence: what the reader finds there. */\n description?: React.ReactNode;\n /** The icon tile on the left (a product mark, a signature icon): decorative, the title names the link. */\n icon?: React.ReactNode;\n /** A short mono line above the title (a category, a version). */\n meta?: React.ReactNode;\n /** The way in, under the text, with an arrow: `Open`, `Read the guide`. */\n cta?: React.ReactNode;\n /** Under the text, before the way in: facts (`Facts`), badges. Never another link — the card is one. */\n extra?: React.ReactNode;\n /**\n * The title's element: a heading when the cards are the content of a section (a hub), `span` inside a list of links.\n * @default \"h3\"\n */\n titleAs?: \"h2\" | \"h3\" | \"h4\" | \"span\";\n /**\n * `row`: the icon beside the text (compact, the default); `stack`: the icon above it.\n * @default \"row\"\n */\n layout?: \"row\" | \"stack\";\n /**\n * Where the facts (`extra`) and the way in (`cta`) sit when the card is taller than its text — a row of cards at one\n * height: `bottom` pins them to the card's foot, so they share a line across the row; `top` leaves them under the text.\n * @default \"bottom\"\n */\n footerAlign?: \"bottom\" | \"top\";\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** An entry point: the whole card is one link — icon, title, sentence, the way in. Lifts and lights under the pointer. */\nexport function LinkCard({ href, title, description, icon, meta, cta, extra, titleAs: Title = \"h3\", layout = \"row\", footerAlign = \"bottom\", linkAs, className, ...props }: LinkCardProps) {\n const external = isExternal(href);\n return (\n <BlockLink\n href={href}\n linkAs={linkAs}\n external={external}\n className={cn(\"kz-block-card kz-link-card\", className)}\n data-kz-light=\"\"\n data-layout={layout === \"row\" ? undefined : layout}\n data-footer={footerAlign === \"top\" ? \"top\" : undefined}\n {...props}\n >\n {icon && (\n <span className=\"kz-block-icon\" aria-hidden>\n {icon}\n </span>\n )}\n <div className=\"kz-link-card-body\">\n {meta && <p className=\"kz-link-card-meta\">{meta}</p>}\n <Title className=\"kz-link-card-title\">{title}</Title>\n {description && <p className=\"kz-link-card-text\">{description}</p>}\n {(extra || cta || external) && (\n <div className=\"kz-link-card-foot\">\n {extra && <div className=\"kz-link-card-extra\">{extra}</div>}\n {(cta || external) && (\n <span className=\"kz-link-card-cta\">\n {cta}\n {external ? <ExternalGlyph className=\"h-3.5 w-3.5\" /> : <ArrowGlyph className=\"h-3.5 w-3.5\" />}\n </span>\n )}\n </div>\n )}\n </div>\n </BlockLink>\n );\n}\n"
|
|
179
187
|
},
|
|
180
188
|
{
|
|
181
189
|
"path": "link-card/meta.ts",
|
|
182
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Link card\",\n summary:\n \"An entry point where the whole card is one link: an icon tile, a title, one sentence and the way in — compact, the same on every page, lifting and lighting under the pointer or the focus.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n whenToUse: [\n \"The entry points of a hub: a documentation's sections, the products, the next steps of a guide.\",\n \"Any link that deserves more than its words: what the reader finds there, in one sentence.\",\n ],\n whenNotToUse: [\n { when: \"When the card holds several actions: a card that is a link holds no other link.\", use: \"argument-card\" },\n { when: \"For a link inside a sentence or a list of plain links.\", use: \"chip-link\" },\n ],\n bestPractices: [\n \"One sentence of description; the title names the destination, the way in (`cta`) says the action.\",\n \"Use the product's mark or a signature icon as `icon`, never a placeholder.\",\n \"Pass the app's router link as `linkAs` (Next's `Link`) so internal links prefetch.\",\n \"In a hub, keep the default `h3` titles under the section's `h2`; in a list of links, `titleAs=\\\"span\\\"`.\",\n ],\n accessibility: {\n keyboard: [{ keys: \"Tab\", action: \"Focuses the card, which lights and draws the focus ring like the pointer does.\" }],\n notes: [\n \"The card is a single link: its accessible name is its whole text — keep it short.\",\n \"An absolute address opens in a new tab with `rel=\\\"noopener noreferrer\\\"` and the external mark.\",\n \"The icon and the arrow are decorative; the lift and the light stop under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"card-grid\", \"glow-card\", \"chip-link\"],\n web: {\n imports: [\"LinkCard\"],\n examples: [\n { name: \"docs-hub\", title: \"A documentation hub\", description: \"Three compact cards in a grid: a mark, a title, a sentence, `Open`.\" },\n { name: \"stack\", title: \"Stacked, with facts\", description: \"`layout=\\\"stack\\\"`, a category above the title and facts under the text.\" },\n { name: \"external\", title: \"An external link\", description: \"An absolute address: a new tab and the external mark.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
190
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Link card\",\n summary:\n \"An entry point where the whole card is one link: an icon tile, a title, one sentence and the way in — compact, the same on every page, lifting and lighting under the pointer or the focus.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n whenToUse: [\n \"The entry points of a hub: a documentation's sections, the products, the next steps of a guide.\",\n \"Any link that deserves more than its words: what the reader finds there, in one sentence.\",\n ],\n whenNotToUse: [\n { when: \"When the card holds several actions: a card that is a link holds no other link.\", use: \"argument-card\" },\n { when: \"For a link inside a sentence or a list of plain links.\", use: \"chip-link\" },\n ],\n bestPractices: [\n \"One sentence of description; the title names the destination, the way in (`cta`) says the action.\",\n \"Use the product's mark or a signature icon as `icon`, never a placeholder.\",\n \"Pass the app's router link as `linkAs` (Next's `Link`) so internal links prefetch.\",\n \"In a row of cards at one height (`CardGrid`), the facts (`extra`) and the way in (`cta`) are pinned to the foot of each card, so they share a line whatever the length of the sentences; `footerAlign=\\\"top\\\"` keeps them under the text.\",\n \"In a hub, keep the default `h3` titles under the section's `h2`; in a list of links, `titleAs=\\\"span\\\"`.\",\n ],\n accessibility: {\n keyboard: [{ keys: \"Tab\", action: \"Focuses the card, which lights and draws the focus ring like the pointer does.\" }],\n notes: [\n \"The card is a single link: its accessible name is its whole text — keep it short.\",\n \"An absolute address opens in a new tab with `rel=\\\"noopener noreferrer\\\"` and the external mark.\",\n \"The icon and the arrow are decorative; the lift and the light stop under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"card-grid\", \"glow-card\", \"chip-link\"],\n web: {\n imports: [\"LinkCard\"],\n examples: [\n { name: \"docs-hub\", title: \"A documentation hub\", description: \"Three compact cards in a grid: a mark, a title, a sentence, `Open`.\" },\n { name: \"stack\", title: \"Stacked, with facts\", description: \"`layout=\\\"stack\\\"`, a category above the title and facts under the text.\" },\n { name: \"external\", title: \"An external link\", description: \"An absolute address: a new tab and the external mark.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
183
191
|
},
|
|
184
192
|
{
|
|
185
193
|
"path": "meta.ts",
|
|
@@ -239,6 +247,13 @@
|
|
|
239
247
|
"description": "Under the text, before the way in: facts (`Facts`), badges. Never another link — the card is one.",
|
|
240
248
|
"required": false
|
|
241
249
|
},
|
|
250
|
+
{
|
|
251
|
+
"name": "footerAlign",
|
|
252
|
+
"type": "\"bottom\" | \"top\"",
|
|
253
|
+
"default": "bottom",
|
|
254
|
+
"description": "Where the facts (`extra`) and the way in (`cta`) sit when the card is taller than its text — a row of cards at one\nheight: `bottom` pins them to the card's foot, so they share a line across the row; `top` leaves them under the text.",
|
|
255
|
+
"required": false
|
|
256
|
+
},
|
|
242
257
|
{
|
|
243
258
|
"name": "icon",
|
|
244
259
|
"type": "ReactNode",
|
package/registry/map.json
CHANGED
package/registry/nav-bar.json
CHANGED