@epicbagel/saas-kit 1.34.0 → 1.36.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/README.md +64 -0
- package/dist/{chunk-DILYV4FI.js → chunk-AL3FBR36.js} +12 -6
- package/dist/{chunk-FARCLWLU.cjs → chunk-AYH5TTLD.cjs} +12 -6
- package/dist/{chunk-HTPPNW74.js → chunk-DMX7LQ6D.js} +1 -1
- package/dist/{chunk-BGCNGYPL.cjs → chunk-I6IFBKCL.cjs} +2 -2
- package/dist/primitives/chat.cjs +3 -3
- package/dist/primitives/chat.js +6 -6
- package/dist/primitives/code-block.cjs +3 -3
- package/dist/primitives/code-block.js +2 -2
- package/dist/primitives/copy-button.cjs +2 -2
- package/dist/primitives/copy-button.d.cts +9 -0
- package/dist/primitives/copy-button.d.ts +9 -0
- package/dist/primitives/copy-button.js +1 -1
- package/dist/primitives/cover-banner.cjs +41 -0
- package/dist/primitives/cover-banner.d.cts +67 -0
- package/dist/primitives/cover-banner.d.ts +67 -0
- package/dist/primitives/cover-banner.js +41 -0
- package/dist/primitives/file-preview.cjs +3 -3
- package/dist/primitives/file-preview.js +2 -2
- package/dist/primitives/tabbed-page-header.cjs +3 -3
- package/dist/primitives/tabbed-page-header.js +3 -3
- package/package.json +11 -1
- package/src/styles/components/cover-banner.css +201 -0
- package/src/styles/components.css +1 -0
- package/src/styles/utilities.css +5 -3
package/README.md
CHANGED
|
@@ -514,6 +514,70 @@ export function Appearance() {
|
|
|
514
514
|
`paletteDescription`. Each palette's card is named by its `label` and
|
|
515
515
|
described by its `description`.
|
|
516
516
|
|
|
517
|
+
## Cover banner (1.35.0)
|
|
518
|
+
|
|
519
|
+
`primitives/cover-banner` is a stock cover: the banner the kit draws for
|
|
520
|
+
anything that has no cover image of its own, so a card without one is as tall
|
|
521
|
+
as its neighbours and doesn't look bare. It is the default banner a profile
|
|
522
|
+
shows before anyone uploads one, in whichever palette the app is in.
|
|
523
|
+
|
|
524
|
+
```tsx
|
|
525
|
+
import { AspectRatio } from "@epicbagel/saas-kit/primitives/aspect-ratio";
|
|
526
|
+
import { CoverBanner } from "@epicbagel/saas-kit/primitives/cover-banner";
|
|
527
|
+
|
|
528
|
+
<AspectRatio ratio={4}>
|
|
529
|
+
{person.coverUrl ? (
|
|
530
|
+
<img src={person.coverUrl} alt="" className="size-full object-cover" />
|
|
531
|
+
) : (
|
|
532
|
+
<CoverBanner seed={person.id} />
|
|
533
|
+
)}
|
|
534
|
+
</AspectRatio>
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
- **Paired with the theme.** Every colour is mixed in CSS from the palette's
|
|
538
|
+
tokens: `primary` into `muted` for the tints, and into `foreground` for a
|
|
539
|
+
faint pattern. So a banner follows the palette, light and dark, and an
|
|
540
|
+
`AccentTheme` around it, with no image, no request and no canvas.
|
|
541
|
+
A black-and-white palette gets a black-and-white banner. Where the browser
|
|
542
|
+
can read a colour's channels, a neighbouring hue is mixed in beside the
|
|
543
|
+
primary's, turned away from yellow-green, where a tint goes muddy; elsewhere
|
|
544
|
+
a banner is one hue.
|
|
545
|
+
- **Six designs** (`COVER_BANNER_VARIANTS`): `wash`, one soft diagonal
|
|
546
|
+
gradient; `glow`, two soft lights; `grid`, a wash under a faint ruled grid;
|
|
547
|
+
`dots`, a wash under a faint dot matrix; `rings`, faint contour lines from
|
|
548
|
+
one corner; `arcs`, broad tonal bands round one corner. Each keeps its
|
|
549
|
+
deeper colour and its pattern on the far side, and its calmest part low on
|
|
550
|
+
the near side, where an avatar usually overlaps it.
|
|
551
|
+
- **A seed picks one.** `seed` is a string or a number, such as a person's id
|
|
552
|
+
or name, and picks the same design every time, on the server and in the
|
|
553
|
+
browser, with no randomness: one person keeps one banner, and a list of
|
|
554
|
+
people isn't a wall of the same one. It is read exactly as given, so pass
|
|
555
|
+
something that stays put, an id before a name. `coverBannerVariant(seed)`
|
|
556
|
+
is the same pick as a function. With no seed it is the first design.
|
|
557
|
+
- **Or name one.** `variant` names the design outright, and wins over `seed`.
|
|
558
|
+
- **It fills its frame.** Put it where the cover image would go, in a frame
|
|
559
|
+
you own, at any size: it is 100% of the frame's width and height. With no
|
|
560
|
+
frame, or one that gives no height, it holds 4:1. `className` and the other
|
|
561
|
+
`div` attributes go to the element, and a utility class wins over its rules.
|
|
562
|
+
- **Quiet.** The deepest tint is still four parts surface, so a face and a
|
|
563
|
+
name stay the loudest things on the card. Give an avatar that overlaps its
|
|
564
|
+
edge a ring in the card's colour (`ring-4 ring-card`), as you would over a
|
|
565
|
+
photograph.
|
|
566
|
+
- **Decoration only.** It is hidden from screen readers and takes no
|
|
567
|
+
children. It is not a placeholder while a real cover loads (that is
|
|
568
|
+
`Skeleton`), not a stand-in for a photo or a logo, and not a surface to set
|
|
569
|
+
text on: its colours promise no contrast to anything laid over it.
|
|
570
|
+
- **Why a banner, when `PersonAvatar` draws no generated tile.** A coloured
|
|
571
|
+
tile masks a missing photo by pretending to be one, so a person with no
|
|
572
|
+
photo gets plain initials. A cover is decoration and carries no identity, so
|
|
573
|
+
a stock one misleads nobody: it stands in for no particular picture, and the
|
|
574
|
+
seed only keeps a list from repeating itself.
|
|
575
|
+
- **Styles.** Its rules are in `styles/utilities.css`
|
|
576
|
+
(`components/cover-banner.css`), on `data-slot="cover-banner"` and each
|
|
577
|
+
design's `data-variant`, so an app that already imports the kit's styles
|
|
578
|
+
needs nothing more. In forced colours it is the plain canvas, with no
|
|
579
|
+
pattern. On paper browsers leave backgrounds out, so it prints blank.
|
|
580
|
+
|
|
517
581
|
## Branded page (1.34.0)
|
|
518
582
|
|
|
519
583
|
`primitives/branded-page` is the whole of a public page one company shows to
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Button
|
|
4
4
|
} from "./chunk-W326GKML.js";
|
|
5
|
-
import {
|
|
6
|
-
cn
|
|
7
|
-
} from "./chunk-G6SRSWXB.js";
|
|
8
5
|
import {
|
|
9
6
|
useCopyToClipboard
|
|
10
7
|
} from "./chunk-ZWZAAGQF.js";
|
|
@@ -13,6 +10,15 @@ import {
|
|
|
13
10
|
import { forwardRef } from "react";
|
|
14
11
|
import { Check, Copy } from "lucide-react";
|
|
15
12
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var ICON_SIZES = /* @__PURE__ */ new Set([
|
|
14
|
+
"icon",
|
|
15
|
+
"icon-lg",
|
|
16
|
+
"icon-sm",
|
|
17
|
+
"icon-md",
|
|
18
|
+
"icon-xs",
|
|
19
|
+
"icon-compact",
|
|
20
|
+
"icon-micro"
|
|
21
|
+
]);
|
|
16
22
|
var CopyButton = forwardRef(
|
|
17
23
|
function CopyButton2({
|
|
18
24
|
text,
|
|
@@ -27,7 +33,7 @@ var CopyButton = forwardRef(
|
|
|
27
33
|
}, ref) {
|
|
28
34
|
const { copied, copy } = useCopyToClipboard({ resetAfter });
|
|
29
35
|
const iconOnly = label == null;
|
|
30
|
-
const resolvedSize = size
|
|
36
|
+
const resolvedSize = iconOnly ? size != null && ICON_SIZES.has(size) ? size : "icon-compact" : size ?? "sm";
|
|
31
37
|
return /* @__PURE__ */ jsxs(
|
|
32
38
|
Button,
|
|
33
39
|
{
|
|
@@ -35,7 +41,7 @@ var CopyButton = forwardRef(
|
|
|
35
41
|
type: "button",
|
|
36
42
|
variant,
|
|
37
43
|
size: resolvedSize,
|
|
38
|
-
className
|
|
44
|
+
className,
|
|
39
45
|
onClick: async (e) => {
|
|
40
46
|
e.stopPropagation();
|
|
41
47
|
const ok = await copy(text);
|
|
@@ -44,7 +50,7 @@ var CopyButton = forwardRef(
|
|
|
44
50
|
"aria-label": iconOnly ? "Copy" : void 0,
|
|
45
51
|
...rest,
|
|
46
52
|
children: [
|
|
47
|
-
copied ? /* @__PURE__ */ jsx(Check, {
|
|
53
|
+
copied ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Copy, {}),
|
|
48
54
|
!iconOnly && (copied ? copiedLabel ?? label : label)
|
|
49
55
|
]
|
|
50
56
|
}
|
|
@@ -4,15 +4,21 @@
|
|
|
4
4
|
var _chunkJDEXZGIYcjs = require('./chunk-JDEXZGIY.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
|
|
8
|
-
|
|
9
|
-
|
|
10
7
|
var _chunk4LXN6GLZcjs = require('./chunk-4LXN6GLZ.cjs');
|
|
11
8
|
|
|
12
9
|
// src/primitives/copy-button.tsx
|
|
13
10
|
var _react = require('react');
|
|
14
11
|
var _lucidereact = require('lucide-react');
|
|
15
12
|
var _jsxruntime = require('react/jsx-runtime');
|
|
13
|
+
var ICON_SIZES = /* @__PURE__ */ new Set([
|
|
14
|
+
"icon",
|
|
15
|
+
"icon-lg",
|
|
16
|
+
"icon-sm",
|
|
17
|
+
"icon-md",
|
|
18
|
+
"icon-xs",
|
|
19
|
+
"icon-compact",
|
|
20
|
+
"icon-micro"
|
|
21
|
+
]);
|
|
16
22
|
var CopyButton = _react.forwardRef.call(void 0,
|
|
17
23
|
function CopyButton2({
|
|
18
24
|
text,
|
|
@@ -27,7 +33,7 @@ var CopyButton = _react.forwardRef.call(void 0,
|
|
|
27
33
|
}, ref) {
|
|
28
34
|
const { copied, copy } = _chunk4LXN6GLZcjs.useCopyToClipboard.call(void 0, { resetAfter });
|
|
29
35
|
const iconOnly = label == null;
|
|
30
|
-
const resolvedSize =
|
|
36
|
+
const resolvedSize = iconOnly ? size != null && ICON_SIZES.has(size) ? size : "icon-compact" : _nullishCoalesce(size, () => ( "sm"));
|
|
31
37
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
32
38
|
_chunkJDEXZGIYcjs.Button,
|
|
33
39
|
{
|
|
@@ -35,7 +41,7 @@ var CopyButton = _react.forwardRef.call(void 0,
|
|
|
35
41
|
type: "button",
|
|
36
42
|
variant,
|
|
37
43
|
size: resolvedSize,
|
|
38
|
-
className
|
|
44
|
+
className,
|
|
39
45
|
onClick: async (e) => {
|
|
40
46
|
e.stopPropagation();
|
|
41
47
|
const ok = await copy(text);
|
|
@@ -44,7 +50,7 @@ var CopyButton = _react.forwardRef.call(void 0,
|
|
|
44
50
|
"aria-label": iconOnly ? "Copy" : void 0,
|
|
45
51
|
...rest,
|
|
46
52
|
children: [
|
|
47
|
-
copied ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Check, {
|
|
53
|
+
copied ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Check, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Copy, {}),
|
|
48
54
|
!iconOnly && (copied ? _nullishCoalesce(copiedLabel, () => ( label)) : label)
|
|
49
55
|
]
|
|
50
56
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkAYH5TTLDcjs = require('./chunk-AYH5TTLD.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
|
|
@@ -38,7 +38,7 @@ function CodeBlock({
|
|
|
38
38
|
}
|
|
39
39
|
),
|
|
40
40
|
copy ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
41
|
-
|
|
41
|
+
_chunkAYH5TTLDcjs.CopyButton,
|
|
42
42
|
{
|
|
43
43
|
text: copyText,
|
|
44
44
|
label: copyLabel,
|
package/dist/primitives/chat.cjs
CHANGED
|
@@ -35,15 +35,15 @@ var _chunkB765DSRGcjs = require('../chunk-B765DSRG.cjs');
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
var _chunk7LDJAXRXcjs = require('../chunk-7LDJAXRX.cjs');
|
|
39
|
+
require('../chunk-2HV2KKOE.cjs');
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
var _chunkDHXKCMKYcjs = require('../chunk-DHXKCMKY.cjs');
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
|
|
45
|
-
var
|
|
46
|
-
require('../chunk-2HV2KKOE.cjs');
|
|
46
|
+
var _chunkDHXKCMKYcjs = require('../chunk-DHXKCMKY.cjs');
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
|
package/dist/primitives/chat.js
CHANGED
|
@@ -31,6 +31,12 @@ import {
|
|
|
31
31
|
Marker,
|
|
32
32
|
MarkerContent
|
|
33
33
|
} from "../chunk-IW4ZIXXG.js";
|
|
34
|
+
import {
|
|
35
|
+
InputGroup,
|
|
36
|
+
InputGroupAddon,
|
|
37
|
+
InputGroupTextarea
|
|
38
|
+
} from "../chunk-QTPPNK6F.js";
|
|
39
|
+
import "../chunk-6FC65AZQ.js";
|
|
34
40
|
import {
|
|
35
41
|
Item,
|
|
36
42
|
ItemContent,
|
|
@@ -38,12 +44,6 @@ import {
|
|
|
38
44
|
ItemMedia,
|
|
39
45
|
ItemTitle
|
|
40
46
|
} from "../chunk-SG4ZE2EN.js";
|
|
41
|
-
import {
|
|
42
|
-
InputGroup,
|
|
43
|
-
InputGroupAddon,
|
|
44
|
-
InputGroupTextarea
|
|
45
|
-
} from "../chunk-QTPPNK6F.js";
|
|
46
|
-
import "../chunk-6FC65AZQ.js";
|
|
47
47
|
import {
|
|
48
48
|
FileDropArea,
|
|
49
49
|
FileDropzone
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
require('../chunk-
|
|
4
|
+
var _chunkI6IFBKCLcjs = require('../chunk-I6IFBKCL.cjs');
|
|
5
|
+
require('../chunk-AYH5TTLD.cjs');
|
|
6
6
|
require('../chunk-JDEXZGIY.cjs');
|
|
7
7
|
require('../chunk-KWLSBTB2.cjs');
|
|
8
8
|
require('../chunk-4LXN6GLZ.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
exports.CodeBlock =
|
|
11
|
+
exports.CodeBlock = _chunkI6IFBKCLcjs.CodeBlock;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
CodeBlock
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-DMX7LQ6D.js";
|
|
5
|
+
import "../chunk-AL3FBR36.js";
|
|
6
6
|
import "../chunk-W326GKML.js";
|
|
7
7
|
import "../chunk-G6SRSWXB.js";
|
|
8
8
|
import "../chunk-ZWZAAGQF.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkAYH5TTLDcjs = require('../chunk-AYH5TTLD.cjs');
|
|
5
5
|
require('../chunk-JDEXZGIY.cjs');
|
|
6
6
|
require('../chunk-KWLSBTB2.cjs');
|
|
7
7
|
require('../chunk-4LXN6GLZ.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
exports.CopyButton =
|
|
10
|
+
exports.CopyButton = _chunkAYH5TTLDcjs.CopyButton;
|
|
@@ -9,6 +9,15 @@ interface CopyButtonProps extends Omit<ButtonProps, "onClick" | "children"> {
|
|
|
9
9
|
text: string;
|
|
10
10
|
/** Optional label shown next to the icon. Omit for an icon-only button. */
|
|
11
11
|
label?: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* The Button size. With a `label` it is a text button, `sm` by default.
|
|
14
|
+
* With no label it is an icon button: `icon-compact` (28px) by default,
|
|
15
|
+
* and any of the Button's icon sizes as given, so beside other icon
|
|
16
|
+
* buttons it takes theirs (`size="icon"`) and shares their centre line. A
|
|
17
|
+
* text size on a button with no text means nothing, and is read as the
|
|
18
|
+
* default.
|
|
19
|
+
*/
|
|
20
|
+
size?: ButtonProps["size"];
|
|
12
21
|
/** Label shown after a successful copy. Defaults to `label`. */
|
|
13
22
|
copiedLabel?: ReactNode;
|
|
14
23
|
/** ms before the success state reverts. Defaults to 2000. */
|
|
@@ -9,6 +9,15 @@ interface CopyButtonProps extends Omit<ButtonProps, "onClick" | "children"> {
|
|
|
9
9
|
text: string;
|
|
10
10
|
/** Optional label shown next to the icon. Omit for an icon-only button. */
|
|
11
11
|
label?: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* The Button size. With a `label` it is a text button, `sm` by default.
|
|
14
|
+
* With no label it is an icon button: `icon-compact` (28px) by default,
|
|
15
|
+
* and any of the Button's icon sizes as given, so beside other icon
|
|
16
|
+
* buttons it takes theirs (`size="icon"`) and shares their centre line. A
|
|
17
|
+
* text size on a button with no text means nothing, and is read as the
|
|
18
|
+
* default.
|
|
19
|
+
*/
|
|
20
|
+
size?: ButtonProps["size"];
|
|
12
21
|
/** Label shown after a successful copy. Defaults to `label`. */
|
|
13
22
|
copiedLabel?: ReactNode;
|
|
14
23
|
/** ms before the success state reverts. Defaults to 2000. */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }"use client";
|
|
2
|
+
|
|
3
|
+
// src/primitives/cover-banner.tsx
|
|
4
|
+
var _react = require('react');
|
|
5
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
6
|
+
var COVER_BANNER_VARIANTS = ["wash", "glow", "grid", "dots", "rings", "arcs"];
|
|
7
|
+
function hashSeed(seed) {
|
|
8
|
+
let hash = 2166136261;
|
|
9
|
+
for (let index = 0; index < seed.length; index++) {
|
|
10
|
+
hash ^= seed.charCodeAt(index);
|
|
11
|
+
hash = Math.imul(hash, 16777619);
|
|
12
|
+
}
|
|
13
|
+
hash ^= hash >>> 16;
|
|
14
|
+
hash = Math.imul(hash, 2246822507);
|
|
15
|
+
hash ^= hash >>> 13;
|
|
16
|
+
hash = Math.imul(hash, 3266489909);
|
|
17
|
+
hash ^= hash >>> 16;
|
|
18
|
+
return hash >>> 0;
|
|
19
|
+
}
|
|
20
|
+
function coverBannerVariant(seed) {
|
|
21
|
+
const text = seed == null ? "" : String(seed);
|
|
22
|
+
if (text === "") return COVER_BANNER_VARIANTS[0];
|
|
23
|
+
return COVER_BANNER_VARIANTS[hashSeed(text) % COVER_BANNER_VARIANTS.length];
|
|
24
|
+
}
|
|
25
|
+
var CoverBanner = _react.forwardRef.call(void 0, function CoverBanner2({ seed, variant, ...props }, ref) {
|
|
26
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
ref,
|
|
30
|
+
...props,
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
"data-slot": "cover-banner",
|
|
33
|
+
"data-variant": _nullishCoalesce(variant, () => ( coverBannerVariant(seed)))
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
exports.COVER_BANNER_VARIANTS = COVER_BANNER_VARIANTS; exports.CoverBanner = CoverBanner; exports.coverBannerVariant = coverBannerVariant;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The stock designs, in the order a seed deals them:
|
|
6
|
+
*
|
|
7
|
+
* - `wash`: one soft diagonal gradient.
|
|
8
|
+
* - `glow`: two soft lights, one low on the near side and one high on the far.
|
|
9
|
+
* - `grid`: a wash under a faint ruled grid.
|
|
10
|
+
* - `dots`: a wash under a faint dot matrix.
|
|
11
|
+
* - `rings`: faint contour lines spreading from the far lower corner.
|
|
12
|
+
* - `arcs`: broad tonal bands curving round the far lower corner.
|
|
13
|
+
*
|
|
14
|
+
* The order and the number are part of the contract: a seed picks by
|
|
15
|
+
* position, so moving, removing or adding a design would deal everyone a
|
|
16
|
+
* different banner.
|
|
17
|
+
*/
|
|
18
|
+
declare const COVER_BANNER_VARIANTS: readonly ["wash", "glow", "grid", "dots", "rings", "arcs"];
|
|
19
|
+
/** One of the stock designs, by name. */
|
|
20
|
+
type CoverBannerVariant = (typeof COVER_BANNER_VARIANTS)[number];
|
|
21
|
+
/**
|
|
22
|
+
* The design a seed picks: the same one every time, on the server and in the
|
|
23
|
+
* browser, with no randomness. The seed is read exactly as given (case and
|
|
24
|
+
* spaces count), so pass something that stays put: an id before a name. A
|
|
25
|
+
* number is read as its digits. No seed (`null`, `undefined` or `""`) is the
|
|
26
|
+
* first design.
|
|
27
|
+
*/
|
|
28
|
+
declare function coverBannerVariant(seed: string | number | null | undefined): CoverBannerVariant;
|
|
29
|
+
interface CoverBannerProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
30
|
+
/**
|
|
31
|
+
* Picks one of the stock designs, the same one every time for the same
|
|
32
|
+
* seed: a person's id, or their name. One person keeps one banner, and a
|
|
33
|
+
* list of people isn't a wall of the same one.
|
|
34
|
+
*/
|
|
35
|
+
seed?: string | number | null;
|
|
36
|
+
/** Names the design outright. Wins over `seed`. */
|
|
37
|
+
variant?: CoverBannerVariant;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A stock cover: the banner the kit draws for anything that has no cover
|
|
41
|
+
* image of its own, so a card without one is the same height as its
|
|
42
|
+
* neighbours and doesn't look bare.
|
|
43
|
+
*
|
|
44
|
+
* It is drawn in CSS from the palette's own tokens (the primary colour mixed
|
|
45
|
+
* into the muted surface, and into the foreground for a faint pattern), so it
|
|
46
|
+
* follows the palette, light and dark, and an `AccentTheme` around it, with
|
|
47
|
+
* no image, no request and no canvas. It fills the frame it is put in; with
|
|
48
|
+
* no frame, or one that gives no height, it holds 4:1. Put it where the
|
|
49
|
+
* cover image would go and it sits quietly behind an avatar and a name.
|
|
50
|
+
*
|
|
51
|
+
* `PersonAvatar` deliberately draws no generated tile, because a coloured
|
|
52
|
+
* tile masks a missing photo by pretending to be one. A banner is different:
|
|
53
|
+
* a cover is decoration and carries no identity, so a stock one misleads
|
|
54
|
+
* nobody. It stands in for no particular picture, and the seed only keeps a
|
|
55
|
+
* list from repeating itself: it says nothing about the person.
|
|
56
|
+
*
|
|
57
|
+
* It is decoration: hidden from screen readers, with no children. It is not
|
|
58
|
+
* a placeholder while a real cover loads (that is `Skeleton`), not a
|
|
59
|
+
* stand-in for a photo or a logo, and not a surface to set text on: its
|
|
60
|
+
* colours promise no contrast to anything laid over it. An avatar that
|
|
61
|
+
* overlaps its edge wants a ring in the card's colour (`ring-4 ring-card`).
|
|
62
|
+
*
|
|
63
|
+
* Its styles are the kit's `styles/utilities.css` (`components/cover-banner.css`).
|
|
64
|
+
*/
|
|
65
|
+
declare const CoverBanner: React.ForwardRefExoticComponent<CoverBannerProps & React.RefAttributes<HTMLDivElement>>;
|
|
66
|
+
|
|
67
|
+
export { COVER_BANNER_VARIANTS, CoverBanner, type CoverBannerProps, type CoverBannerVariant, coverBannerVariant };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The stock designs, in the order a seed deals them:
|
|
6
|
+
*
|
|
7
|
+
* - `wash`: one soft diagonal gradient.
|
|
8
|
+
* - `glow`: two soft lights, one low on the near side and one high on the far.
|
|
9
|
+
* - `grid`: a wash under a faint ruled grid.
|
|
10
|
+
* - `dots`: a wash under a faint dot matrix.
|
|
11
|
+
* - `rings`: faint contour lines spreading from the far lower corner.
|
|
12
|
+
* - `arcs`: broad tonal bands curving round the far lower corner.
|
|
13
|
+
*
|
|
14
|
+
* The order and the number are part of the contract: a seed picks by
|
|
15
|
+
* position, so moving, removing or adding a design would deal everyone a
|
|
16
|
+
* different banner.
|
|
17
|
+
*/
|
|
18
|
+
declare const COVER_BANNER_VARIANTS: readonly ["wash", "glow", "grid", "dots", "rings", "arcs"];
|
|
19
|
+
/** One of the stock designs, by name. */
|
|
20
|
+
type CoverBannerVariant = (typeof COVER_BANNER_VARIANTS)[number];
|
|
21
|
+
/**
|
|
22
|
+
* The design a seed picks: the same one every time, on the server and in the
|
|
23
|
+
* browser, with no randomness. The seed is read exactly as given (case and
|
|
24
|
+
* spaces count), so pass something that stays put: an id before a name. A
|
|
25
|
+
* number is read as its digits. No seed (`null`, `undefined` or `""`) is the
|
|
26
|
+
* first design.
|
|
27
|
+
*/
|
|
28
|
+
declare function coverBannerVariant(seed: string | number | null | undefined): CoverBannerVariant;
|
|
29
|
+
interface CoverBannerProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
30
|
+
/**
|
|
31
|
+
* Picks one of the stock designs, the same one every time for the same
|
|
32
|
+
* seed: a person's id, or their name. One person keeps one banner, and a
|
|
33
|
+
* list of people isn't a wall of the same one.
|
|
34
|
+
*/
|
|
35
|
+
seed?: string | number | null;
|
|
36
|
+
/** Names the design outright. Wins over `seed`. */
|
|
37
|
+
variant?: CoverBannerVariant;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A stock cover: the banner the kit draws for anything that has no cover
|
|
41
|
+
* image of its own, so a card without one is the same height as its
|
|
42
|
+
* neighbours and doesn't look bare.
|
|
43
|
+
*
|
|
44
|
+
* It is drawn in CSS from the palette's own tokens (the primary colour mixed
|
|
45
|
+
* into the muted surface, and into the foreground for a faint pattern), so it
|
|
46
|
+
* follows the palette, light and dark, and an `AccentTheme` around it, with
|
|
47
|
+
* no image, no request and no canvas. It fills the frame it is put in; with
|
|
48
|
+
* no frame, or one that gives no height, it holds 4:1. Put it where the
|
|
49
|
+
* cover image would go and it sits quietly behind an avatar and a name.
|
|
50
|
+
*
|
|
51
|
+
* `PersonAvatar` deliberately draws no generated tile, because a coloured
|
|
52
|
+
* tile masks a missing photo by pretending to be one. A banner is different:
|
|
53
|
+
* a cover is decoration and carries no identity, so a stock one misleads
|
|
54
|
+
* nobody. It stands in for no particular picture, and the seed only keeps a
|
|
55
|
+
* list from repeating itself: it says nothing about the person.
|
|
56
|
+
*
|
|
57
|
+
* It is decoration: hidden from screen readers, with no children. It is not
|
|
58
|
+
* a placeholder while a real cover loads (that is `Skeleton`), not a
|
|
59
|
+
* stand-in for a photo or a logo, and not a surface to set text on: its
|
|
60
|
+
* colours promise no contrast to anything laid over it. An avatar that
|
|
61
|
+
* overlaps its edge wants a ring in the card's colour (`ring-4 ring-card`).
|
|
62
|
+
*
|
|
63
|
+
* Its styles are the kit's `styles/utilities.css` (`components/cover-banner.css`).
|
|
64
|
+
*/
|
|
65
|
+
declare const CoverBanner: React.ForwardRefExoticComponent<CoverBannerProps & React.RefAttributes<HTMLDivElement>>;
|
|
66
|
+
|
|
67
|
+
export { COVER_BANNER_VARIANTS, CoverBanner, type CoverBannerProps, type CoverBannerVariant, coverBannerVariant };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/primitives/cover-banner.tsx
|
|
4
|
+
import { forwardRef } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
var COVER_BANNER_VARIANTS = ["wash", "glow", "grid", "dots", "rings", "arcs"];
|
|
7
|
+
function hashSeed(seed) {
|
|
8
|
+
let hash = 2166136261;
|
|
9
|
+
for (let index = 0; index < seed.length; index++) {
|
|
10
|
+
hash ^= seed.charCodeAt(index);
|
|
11
|
+
hash = Math.imul(hash, 16777619);
|
|
12
|
+
}
|
|
13
|
+
hash ^= hash >>> 16;
|
|
14
|
+
hash = Math.imul(hash, 2246822507);
|
|
15
|
+
hash ^= hash >>> 13;
|
|
16
|
+
hash = Math.imul(hash, 3266489909);
|
|
17
|
+
hash ^= hash >>> 16;
|
|
18
|
+
return hash >>> 0;
|
|
19
|
+
}
|
|
20
|
+
function coverBannerVariant(seed) {
|
|
21
|
+
const text = seed == null ? "" : String(seed);
|
|
22
|
+
if (text === "") return COVER_BANNER_VARIANTS[0];
|
|
23
|
+
return COVER_BANNER_VARIANTS[hashSeed(text) % COVER_BANNER_VARIANTS.length];
|
|
24
|
+
}
|
|
25
|
+
var CoverBanner = forwardRef(function CoverBanner2({ seed, variant, ...props }, ref) {
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
ref,
|
|
30
|
+
...props,
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
"data-slot": "cover-banner",
|
|
33
|
+
"data-variant": variant ?? coverBannerVariant(seed)
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
export {
|
|
38
|
+
COVER_BANNER_VARIANTS,
|
|
39
|
+
CoverBanner,
|
|
40
|
+
coverBannerVariant
|
|
41
|
+
};
|
|
@@ -17,8 +17,8 @@ require('../chunk-ELPV44S6.cjs');
|
|
|
17
17
|
require('../chunk-2BHI5ZWJ.cjs');
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
var
|
|
21
|
-
require('../chunk-
|
|
20
|
+
var _chunkI6IFBKCLcjs = require('../chunk-I6IFBKCL.cjs');
|
|
21
|
+
require('../chunk-AYH5TTLD.cjs');
|
|
22
22
|
require('../chunk-WDXEJDMO.cjs');
|
|
23
23
|
|
|
24
24
|
|
|
@@ -281,7 +281,7 @@ function FilePreviewBody({
|
|
|
281
281
|
kind === "markdown" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkKENBU3NVcjs.LazyMarkdown, { components: { a: NewTabLink, ...markdownComponents }, children: text.text }) : (
|
|
282
282
|
// The theme's relaxed leading is a fixed 1.8rem, which spaces a
|
|
283
283
|
// file's small monospace lines apart; read it at 20px lines.
|
|
284
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
284
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkI6IFBKCLcjs.CodeBlock, { wrap: true, className: "[&>pre]:leading-5", children: text.text })
|
|
285
285
|
)
|
|
286
286
|
] });
|
|
287
287
|
}
|
|
@@ -17,8 +17,8 @@ import "../chunk-INNGGLMZ.js";
|
|
|
17
17
|
import "../chunk-4HWCNRK4.js";
|
|
18
18
|
import {
|
|
19
19
|
CodeBlock
|
|
20
|
-
} from "../chunk-
|
|
21
|
-
import "../chunk-
|
|
20
|
+
} from "../chunk-DMX7LQ6D.js";
|
|
21
|
+
import "../chunk-AL3FBR36.js";
|
|
22
22
|
import "../chunk-3QT5VYZ3.js";
|
|
23
23
|
import {
|
|
24
24
|
Text
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
require('../chunk-DFB4ILN6.cjs');
|
|
5
|
+
var _chunkY6X6XDHEcjs = require('../chunk-Y6X6XDHE.cjs');
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
var
|
|
8
|
+
var _chunkB54ZEKBGcjs = require('../chunk-B54ZEKBG.cjs');
|
|
9
|
+
require('../chunk-DFB4ILN6.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
var _chunkKWLSBTB2cjs = require('../chunk-KWLSBTB2.cjs');
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
|
+
import {
|
|
4
|
+
PageHeader
|
|
5
|
+
} from "../chunk-VWOWPNKT.js";
|
|
3
6
|
import {
|
|
4
7
|
PageTabs
|
|
5
8
|
} from "../chunk-XH2I3YJ6.js";
|
|
6
9
|
import "../chunk-7CRDYV3C.js";
|
|
7
|
-
import {
|
|
8
|
-
PageHeader
|
|
9
|
-
} from "../chunk-VWOWPNKT.js";
|
|
10
10
|
import {
|
|
11
11
|
cn
|
|
12
12
|
} from "../chunk-G6SRSWXB.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicbagel/saas-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.36.0",
|
|
4
4
|
"description": "Accessible React primitives, layout components and design tokens for SaaS apps, built on Radix UI and Tailwind CSS v4.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -490,6 +490,16 @@
|
|
|
490
490
|
"default": "./dist/primitives/count-badge.cjs"
|
|
491
491
|
}
|
|
492
492
|
},
|
|
493
|
+
"./primitives/cover-banner": {
|
|
494
|
+
"import": {
|
|
495
|
+
"types": "./dist/primitives/cover-banner.d.ts",
|
|
496
|
+
"default": "./dist/primitives/cover-banner.js"
|
|
497
|
+
},
|
|
498
|
+
"require": {
|
|
499
|
+
"types": "./dist/primitives/cover-banner.d.cts",
|
|
500
|
+
"default": "./dist/primitives/cover-banner.cjs"
|
|
501
|
+
}
|
|
502
|
+
},
|
|
493
503
|
"./primitives/cycling-prompt": {
|
|
494
504
|
"import": {
|
|
495
505
|
"types": "./dist/primitives/cycling-prompt.d.ts",
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/* Cover banner — the presentation of primitives/cover-banner.tsx: a stock
|
|
2
|
+
* cover for anything with no cover image of its own. Every selector lands on
|
|
3
|
+
* the banner's data-slot, and each design on its data-variant.
|
|
4
|
+
*
|
|
5
|
+
* Every colour is mixed from the palette's tokens: the primary colour into
|
|
6
|
+
* the muted surface for the tints, and into the foreground for a pattern's
|
|
7
|
+
* ink. So a banner follows the palette, light and dark, and an AccentTheme
|
|
8
|
+
* around it, and nothing here names a mode, a palette or a colour. In the
|
|
9
|
+
* components layer, so a consumer's utility classes still win. Part of
|
|
10
|
+
* utilities.css. */
|
|
11
|
+
@layer components {
|
|
12
|
+
[data-slot="cover-banner"] {
|
|
13
|
+
/* The palette's colours, whole (the bare tokens are H S% L% triplets):
|
|
14
|
+
* the surface a banner is made of, the brand's colour that tints it, and
|
|
15
|
+
* a neighbour of that colour, which is the brand's own until the block
|
|
16
|
+
* below turns its hue. */
|
|
17
|
+
--color-cover-banner-surface: var(--color-muted);
|
|
18
|
+
--color-cover-banner-brand: var(--color-primary);
|
|
19
|
+
--color-cover-banner-near: var(--color-primary);
|
|
20
|
+
/* How far round the hue wheel the neighbour sits, in degrees at most.
|
|
21
|
+
* Each design sets its own, so two banners side by side differ in more
|
|
22
|
+
* than their shapes. */
|
|
23
|
+
--cover-banner-turn: 20;
|
|
24
|
+
|
|
25
|
+
/* The surface with the brand's colour in it, at four depths, and with
|
|
26
|
+
* its neighbour at two. The deepest is still four parts surface: a
|
|
27
|
+
* banner sits behind a face and a name. The surface is named first, so
|
|
28
|
+
* the fallback Tailwind writes for a browser without color-mix() is the
|
|
29
|
+
* plain surface. */
|
|
30
|
+
--color-cover-banner-1: color-mix(in oklab, var(--color-cover-banner-surface) 95%, var(--color-cover-banner-brand));
|
|
31
|
+
--color-cover-banner-2: color-mix(in oklab, var(--color-cover-banner-surface) 90%, var(--color-cover-banner-brand));
|
|
32
|
+
--color-cover-banner-3: color-mix(in oklab, var(--color-cover-banner-surface) 84%, var(--color-cover-banner-brand));
|
|
33
|
+
--color-cover-banner-4: color-mix(in oklab, var(--color-cover-banner-surface) 78%, var(--color-cover-banner-brand));
|
|
34
|
+
--color-cover-banner-near-2: color-mix(in oklab, var(--color-cover-banner-surface) 90%, var(--color-cover-banner-near));
|
|
35
|
+
--color-cover-banner-near-4: color-mix(in oklab, var(--color-cover-banner-surface) 78%, var(--color-cover-banner-near));
|
|
36
|
+
/* A pattern's ink: the text's colour and the brand's in equal parts,
|
|
37
|
+
* then mostly see-through. A rule covers more of the banner than a ring,
|
|
38
|
+
* and a ring more than a dot, so each is fainter than the next. */
|
|
39
|
+
--color-cover-banner-ink: color-mix(in oklab, var(--color-foreground) 50%, var(--color-cover-banner-brand));
|
|
40
|
+
--color-cover-banner-rule: color-mix(in oklab, transparent 91%, var(--color-cover-banner-ink));
|
|
41
|
+
--color-cover-banner-ring: color-mix(in oklab, transparent 86%, var(--color-cover-banner-ink));
|
|
42
|
+
--color-cover-banner-dot: color-mix(in oklab, transparent 78%, var(--color-cover-banner-ink));
|
|
43
|
+
|
|
44
|
+
position: relative;
|
|
45
|
+
display: block;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
/* Fills the frame it is put in. Where the frame gives no height, a
|
|
48
|
+
* percentage height is auto, and the ratio holds 4:1 instead. */
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 100%;
|
|
51
|
+
aspect-ratio: 4 / 1;
|
|
52
|
+
background-color: var(--color-cover-banner-1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Where the browser can read a colour's channels, two refinements.
|
|
56
|
+
*
|
|
57
|
+
* A little more chroma, so a coloured brand tints the surface as clearly
|
|
58
|
+
* as a black one darkens it.
|
|
59
|
+
*
|
|
60
|
+
* And the neighbour's hue turned, by up to --cover-banner-turn degrees,
|
|
61
|
+
* always away from yellow-green (hue 110), where a tint goes muddy: a warm
|
|
62
|
+
* colour leans towards red, a cool one towards blue and on to pink, and a
|
|
63
|
+
* colour at 110 stays put. The sine is what sets the direction and eases
|
|
64
|
+
* the amount.
|
|
65
|
+
*
|
|
66
|
+
* A grey has no chroma to raise or turn, so a black-and-white palette
|
|
67
|
+
* stays black and white. Elsewhere a banner is the plain mixes above, in
|
|
68
|
+
* one hue. */
|
|
69
|
+
@supports (color: oklch(from red l calc(c * 1.3) calc(h + 20 * sin((h - 110) * 0.5deg)))) {
|
|
70
|
+
[data-slot="cover-banner"] {
|
|
71
|
+
--color-cover-banner-brand: oklch(from var(--color-primary) l calc(c * 1.3) h);
|
|
72
|
+
--color-cover-banner-near: oklch(
|
|
73
|
+
from var(--color-primary) l calc(c * 1.3) calc(h + var(--cover-banner-turn) * sin((h - 110) * 0.5deg))
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* A pattern is its own layer, so it can fade out across the banner. */
|
|
79
|
+
[data-slot="cover-banner"]::after {
|
|
80
|
+
position: absolute;
|
|
81
|
+
inset: 0;
|
|
82
|
+
pointer-events: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Each design keeps its deeper colour and its pattern on the far side, and
|
|
86
|
+
* its calmest part low on the near side, where an avatar overlaps it. */
|
|
87
|
+
|
|
88
|
+
/* One soft diagonal gradient, from the brand's colour to its neighbour. */
|
|
89
|
+
[data-slot="cover-banner"][data-variant="wash"] {
|
|
90
|
+
--cover-banner-turn: 20;
|
|
91
|
+
background-image: linear-gradient(
|
|
92
|
+
112deg,
|
|
93
|
+
var(--color-cover-banner-1) 0%,
|
|
94
|
+
var(--color-cover-banner-2) 42%,
|
|
95
|
+
var(--color-cover-banner-near-4) 100%
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Two soft lights: the brand's low on the near side, its neighbour's high
|
|
100
|
+
* on the far side. */
|
|
101
|
+
[data-slot="cover-banner"][data-variant="glow"] {
|
|
102
|
+
--cover-banner-turn: 34;
|
|
103
|
+
background-image:
|
|
104
|
+
radial-gradient(ellipse 62% 150% at 4% 122%, var(--color-cover-banner-4), transparent 72%),
|
|
105
|
+
radial-gradient(ellipse 58% 150% at 99% -26%, var(--color-cover-banner-near-4), transparent 72%);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* A wash under a ruled grid, which fades out towards the near side. */
|
|
109
|
+
[data-slot="cover-banner"][data-variant="grid"] {
|
|
110
|
+
--cover-banner-turn: 18;
|
|
111
|
+
background-image: linear-gradient(
|
|
112
|
+
100deg,
|
|
113
|
+
var(--color-cover-banner-1) 0%,
|
|
114
|
+
var(--color-cover-banner-2) 55%,
|
|
115
|
+
var(--color-cover-banner-near-4) 100%
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-slot="cover-banner"][data-variant="grid"]::after {
|
|
120
|
+
content: "";
|
|
121
|
+
background-image:
|
|
122
|
+
linear-gradient(to right, var(--color-cover-banner-rule) 1px, transparent 1px),
|
|
123
|
+
linear-gradient(to bottom, var(--color-cover-banner-rule) 1px, transparent 1px);
|
|
124
|
+
background-size: 24px 24px;
|
|
125
|
+
background-position: center;
|
|
126
|
+
-webkit-mask-image: linear-gradient(100deg, transparent 8%, #000 100%);
|
|
127
|
+
mask-image: linear-gradient(100deg, transparent 8%, #000 100%);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* A wash under a dot matrix, which fades out from the far upper corner. */
|
|
131
|
+
[data-slot="cover-banner"][data-variant="dots"] {
|
|
132
|
+
--cover-banner-turn: 28;
|
|
133
|
+
background-image: linear-gradient(
|
|
134
|
+
200deg,
|
|
135
|
+
var(--color-cover-banner-near-4) 0%,
|
|
136
|
+
var(--color-cover-banner-2) 46%,
|
|
137
|
+
var(--color-cover-banner-1) 100%
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
[data-slot="cover-banner"][data-variant="dots"]::after {
|
|
142
|
+
content: "";
|
|
143
|
+
background-image: radial-gradient(circle, var(--color-cover-banner-dot) 1.1px, transparent 1.7px);
|
|
144
|
+
background-size: 14px 14px;
|
|
145
|
+
background-position: center;
|
|
146
|
+
-webkit-mask-image: radial-gradient(ellipse 86% 160% at 100% 0%, #000 8%, transparent 80%);
|
|
147
|
+
mask-image: radial-gradient(ellipse 86% 160% at 100% 0%, #000 8%, transparent 80%);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Contour lines spreading from under the far lower corner. */
|
|
151
|
+
[data-slot="cover-banner"][data-variant="rings"] {
|
|
152
|
+
--cover-banner-turn: 24;
|
|
153
|
+
background-image: linear-gradient(
|
|
154
|
+
250deg,
|
|
155
|
+
var(--color-cover-banner-3) 0%,
|
|
156
|
+
var(--color-cover-banner-near-2) 40%,
|
|
157
|
+
var(--color-cover-banner-1) 100%
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Each line eases in and out over three pixels, so it stays smooth where
|
|
162
|
+
* the browser draws a hard stop jagged. */
|
|
163
|
+
[data-slot="cover-banner"][data-variant="rings"]::after {
|
|
164
|
+
content: "";
|
|
165
|
+
background-image: repeating-radial-gradient(
|
|
166
|
+
circle at 84% 140%,
|
|
167
|
+
transparent 0,
|
|
168
|
+
transparent 18.5px,
|
|
169
|
+
var(--color-cover-banner-ring) 20px,
|
|
170
|
+
transparent 21.5px
|
|
171
|
+
);
|
|
172
|
+
-webkit-mask-image: radial-gradient(ellipse 84% 200% at 84% 140%, #000 10%, transparent 76%);
|
|
173
|
+
mask-image: radial-gradient(ellipse 84% 200% at 84% 140%, #000 10%, transparent 76%);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Broad tonal bands curving round the far lower corner, deepest inside. */
|
|
177
|
+
[data-slot="cover-banner"][data-variant="arcs"] {
|
|
178
|
+
--cover-banner-turn: 16;
|
|
179
|
+
background-image: radial-gradient(
|
|
180
|
+
circle at 100% 150%,
|
|
181
|
+
var(--color-cover-banner-near-4) 0%,
|
|
182
|
+
var(--color-cover-banner-near-4) 24%,
|
|
183
|
+
var(--color-cover-banner-3) 24.2%,
|
|
184
|
+
var(--color-cover-banner-3) 43%,
|
|
185
|
+
var(--color-cover-banner-2) 43.2%,
|
|
186
|
+
var(--color-cover-banner-2) 64%,
|
|
187
|
+
var(--color-cover-banner-1) 64.2%
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Someone who asked for their own colours gets them, without a pattern. */
|
|
192
|
+
@media (forced-colors: active) {
|
|
193
|
+
[data-slot="cover-banner"] {
|
|
194
|
+
background: Canvas;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
[data-slot="cover-banner"]::after {
|
|
198
|
+
content: none;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/styles/utilities.css
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* Palette-free utilities and component classes: shadcn's data-* variants,
|
|
2
2
|
* no-scrollbar, scroll-fade and shimmer; the cn-attachment, cn-bubble,
|
|
3
3
|
* cn-marker, cn-message and cn-message-scroller classes; the poster
|
|
4
|
-
* family's data-slot rules; and the kit's keyframes.
|
|
4
|
+
* family's and the cover banner's data-slot rules; and the kit's keyframes.
|
|
5
|
+
* Defines no palette and no @source.
|
|
5
6
|
* The component classes @apply the semantic colours (bg-muted,
|
|
6
|
-
* text-muted-foreground, ring-ring, …),
|
|
7
|
-
* them — tokens.css
|
|
7
|
+
* text-muted-foreground, ring-ring, …), and the cover banner mixes its
|
|
8
|
+
* colours from them, so the host's @theme must define them — tokens.css
|
|
9
|
+
* does. */
|
|
8
10
|
@import "./shadcn.css";
|
|
9
11
|
@import "./components.css";
|
|
10
12
|
@import "./keyframes.css";
|