@justai/cuts 0.9.0 → 0.10.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/package.json +3 -2
- package/src/Profile.astro +91 -0
- package/src/Toolbar.astro +6 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justai/cuts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "A persona's named parts — the page shell that holds them, and the cuts themselves.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"./LangPicker.astro": "./src/LangPicker.astro",
|
|
18
18
|
"./BackToZone.astro": "./src/BackToZone.astro",
|
|
19
19
|
"./Posts.astro": "./src/Posts.astro",
|
|
20
|
-
"./Toolbar.astro": "./src/Toolbar.astro"
|
|
20
|
+
"./Toolbar.astro": "./src/Toolbar.astro",
|
|
21
|
+
"./Profile.astro": "./src/Profile.astro"
|
|
21
22
|
},
|
|
22
23
|
"peerDependencies": {
|
|
23
24
|
"@justai/ui": ">=0.2.0",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
// ░░ PROFILE cut — the persona's face: a cover, an avatar, its name · @handle · bio · meta. ░░
|
|
3
|
+
//
|
|
4
|
+
// Structurally identical in every persona; only the CONTENT and a handful of colours differ. It lives
|
|
5
|
+
// here now. A persona writes:
|
|
6
|
+
//
|
|
7
|
+
// <Profile name={t.title} handle="@qr" bio={t.bio} meta={t.meta}
|
|
8
|
+
// avatarLabel="QR code linking to qr.app.zone"
|
|
9
|
+
// cover="linear-gradient(135deg, …)" avatarBg="#fff" avatarPad="7px">
|
|
10
|
+
// <Fragment slot="avatar" set:html={avatarSvg} />
|
|
11
|
+
// </Profile>
|
|
12
|
+
//
|
|
13
|
+
// PROPS carry resolved strings (same convention as Posts/Toolbar/LangPicker — never the whole `t`).
|
|
14
|
+
// The per-persona COLOURS ride in as CSS custom properties (set inline from props), so the shared
|
|
15
|
+
// scoped CSS can theme itself without a variant per persona:
|
|
16
|
+
// --cover the cover background (any CSS <image>: a linear- or radial-gradient)
|
|
17
|
+
// --cover-h cover height (default 132px; the clock personas use 118px)
|
|
18
|
+
// --avatar-bg the avatar tile background (a colour OR a gradient)
|
|
19
|
+
// --avatar-bg-light optional light-scheme override for the tile (whatismyip inverts its icon ground)
|
|
20
|
+
// --avatar-pad padding inside the tile (a QR needs a quiet zone; an icon that bleeds needs 0)
|
|
21
|
+
// --edge inherited from the persona's page padding (16px default; time pads at 20px) so the
|
|
22
|
+
// full-bleed cover reaches the SAME edge as the body — see Toolbar for the same knob.
|
|
23
|
+
//
|
|
24
|
+
// The AVATAR content is a slot (an <img>, or a build-time SVG via set:html) because it is the most
|
|
25
|
+
// persona-specific thing on the page; the tile that frames it is shared. `avatarLabel` gives the tile
|
|
26
|
+
// its accessible name (role="img"); omit it and the tile is inert.
|
|
27
|
+
//
|
|
28
|
+
// CONTRACT: the shell's scroll-spy reads `.pname` and `.handle` from here to name the title strip, and
|
|
29
|
+
// the section id `#profile` is one of the three cut anchors. Nothing else depends on the profile.
|
|
30
|
+
interface Props {
|
|
31
|
+
name: string; // the persona name (the <h1>)
|
|
32
|
+
handle: string; // the @handle line, e.g. "@qr"
|
|
33
|
+
bio: string; // the one-line bio
|
|
34
|
+
meta?: string; // the small meta line under the bio (optional)
|
|
35
|
+
avatarLabel?: string; // accessible name for the avatar tile (role="img"); omit → inert tile
|
|
36
|
+
cover?: string; // CSS background for .cover
|
|
37
|
+
coverHeight?: string; // e.g. "118px"
|
|
38
|
+
avatarBg?: string; // CSS background for the avatar tile
|
|
39
|
+
avatarBgLight?: string;// light-scheme override for the avatar tile
|
|
40
|
+
avatarPad?: string; // padding inside the avatar tile, e.g. "7px"
|
|
41
|
+
}
|
|
42
|
+
const { name, handle, bio, meta, avatarLabel, cover, coverHeight, avatarBg, avatarBgLight, avatarPad } = Astro.props;
|
|
43
|
+
const vars = [
|
|
44
|
+
cover && `--cover:${cover}`,
|
|
45
|
+
coverHeight && `--cover-h:${coverHeight}`,
|
|
46
|
+
avatarBg && `--avatar-bg:${avatarBg}`,
|
|
47
|
+
avatarBgLight && `--avatar-bg-light:${avatarBgLight}`,
|
|
48
|
+
avatarPad && `--avatar-pad:${avatarPad}`,
|
|
49
|
+
].filter(Boolean).join(";");
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
<section class="cut profile" id="profile" style={vars || undefined}>
|
|
53
|
+
<div class="cover"></div>
|
|
54
|
+
<div class="pbody">
|
|
55
|
+
<div class="avatar" role={avatarLabel ? "img" : undefined} aria-label={avatarLabel}><slot name="avatar" /></div>
|
|
56
|
+
<h1 class="pname">{name}</h1>
|
|
57
|
+
<p class="handle">{handle}</p>
|
|
58
|
+
<p class="bio">{bio}</p>
|
|
59
|
+
{meta && <p class="meta">{meta}</p>}
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
/* ---- PROFILE cut ---- */
|
|
65
|
+
.profile { padding-block-end: 8px; }
|
|
66
|
+
.cover {
|
|
67
|
+
height: var(--cover-h, 132px);
|
|
68
|
+
/* full-bleed to the persona's page edge (see --edge on Toolbar) */
|
|
69
|
+
margin-inline: calc(-1 * max(var(--edge, 16px), env(safe-area-inset-left))) calc(-1 * max(var(--edge, 16px), env(safe-area-inset-right)));
|
|
70
|
+
background: var(--cover, linear-gradient(135deg, var(--accent), transparent));
|
|
71
|
+
position: relative;
|
|
72
|
+
}
|
|
73
|
+
@media (min-width: 768px) { .cover { margin-inline: -28px; } }
|
|
74
|
+
.avatar {
|
|
75
|
+
width: 76px; height: 76px; border-radius: var(--radius-lg); margin-block-start: -40px;
|
|
76
|
+
display: grid; place-items: center; padding: var(--avatar-pad, 0);
|
|
77
|
+
background: var(--avatar-bg, #fff);
|
|
78
|
+
border: 3px solid var(--bg); box-shadow: 0 8px 22px -16px rgba(0, 0, 0, 0.22);
|
|
79
|
+
position: relative;
|
|
80
|
+
}
|
|
81
|
+
/* the avatar content is slotted (persona-owned) → style it globally */
|
|
82
|
+
.avatar :global(svg) { width: 100%; height: 100%; display: block; }
|
|
83
|
+
.avatar :global(img) { width: 100%; height: 100%; display: block; object-fit: cover; }
|
|
84
|
+
@media (prefers-color-scheme: light) {
|
|
85
|
+
.avatar { border-color: #fff; background: var(--avatar-bg-light, var(--avatar-bg, #fff)); }
|
|
86
|
+
}
|
|
87
|
+
.pname { font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.02em; margin-block-start: 12px; }
|
|
88
|
+
.handle { color: var(--text-dim); font-size: var(--fs-xs); margin-block-start: 2px; }
|
|
89
|
+
.bio { font-size: var(--fs-sm); line-height: 1.5; margin-block-start: 12px; }
|
|
90
|
+
.meta { color: var(--text-dim); font-size: var(--fs-2xs); margin-block-start: 10px; }
|
|
91
|
+
</style>
|
package/src/Toolbar.astro
CHANGED
|
@@ -72,11 +72,13 @@ const { sectionsLabel, profileLabel, kernelLabel, postsLabel } = Astro.props;
|
|
|
72
72
|
.toolbar {
|
|
73
73
|
position: sticky; top: 0; z-index: 40;
|
|
74
74
|
display: flex; flex-direction: column;
|
|
75
|
-
/* full-bleed: the bar (frosted bg + hairline) touches the contained-view edges; the
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
/* full-bleed: the bar (frosted bg + hairline) touches the contained-view edges; the icons stay
|
|
76
|
+
aligned with the body via matching inline padding. --edge is the persona's page inline padding
|
|
77
|
+
(16px default; a persona that pads its body differently — e.g. time at 20px — sets --edge so the
|
|
78
|
+
bar bleeds to the SAME edge as its content instead of a hardcoded 16px). */
|
|
79
|
+
margin-inline: calc(-1 * max(var(--edge, 16px), env(safe-area-inset-left))) calc(-1 * max(var(--edge, 16px), env(safe-area-inset-right)));
|
|
78
80
|
padding-block: max(8px, env(safe-area-inset-top)) 8px;
|
|
79
|
-
padding-inline: max(16px, env(safe-area-inset-left)) max(16px, env(safe-area-inset-right));
|
|
81
|
+
padding-inline: max(var(--edge, 16px), env(safe-area-inset-left)) max(var(--edge, 16px), env(safe-area-inset-right));
|
|
80
82
|
background: color-mix(in srgb, var(--bg) 70%, transparent);
|
|
81
83
|
backdrop-filter: blur(22px) saturate(1.5);
|
|
82
84
|
-webkit-backdrop-filter: blur(22px) saturate(1.5);
|