@skipleague/design 0.6.0 → 0.7.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/dist/AppLogo.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  /** A SkipLeague app's logo glyph (monoline, drawn on the brand tile). */
2
2
  export type AppGlyph = "lists" | "racquetball" | "trips" | "gifts" | "reading" | "today" | "guide" | "flow";
3
+ /** Every app that has a glyph, for slug→glyph resolution and fallbacks. */
4
+ export declare const APP_GLYPHS: readonly AppGlyph[];
5
+ /**
6
+ * Resolve a platform app slug (e.g. `"skipracquetball"`) to its {@link AppGlyph}
7
+ * by dropping the `skip` prefix, or `null` when no glyph exists for it (so a
8
+ * caller can fall back to a letter {@link AppBadge}). Case-insensitive.
9
+ */
10
+ export declare function appGlyphForSlug(slug: string): AppGlyph | null;
3
11
  /**
4
12
  * The per-app logo mark — a brand-green rounded tile holding a white monoline
5
13
  * glyph. Replaces the single-letter {@link AppBadge}; gives each SkipLeague app a
package/dist/AppLogo.js CHANGED
@@ -1,4 +1,24 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Every app that has a glyph, for slug→glyph resolution and fallbacks. */
3
+ export const APP_GLYPHS = [
4
+ "lists",
5
+ "racquetball",
6
+ "trips",
7
+ "gifts",
8
+ "reading",
9
+ "today",
10
+ "guide",
11
+ "flow",
12
+ ];
13
+ /**
14
+ * Resolve a platform app slug (e.g. `"skipracquetball"`) to its {@link AppGlyph}
15
+ * by dropping the `skip` prefix, or `null` when no glyph exists for it (so a
16
+ * caller can fall back to a letter {@link AppBadge}). Case-insensitive.
17
+ */
18
+ export function appGlyphForSlug(slug) {
19
+ const key = slug.replace(/^skip/i, "").toLowerCase();
20
+ return APP_GLYPHS.includes(key) ? key : null;
21
+ }
2
22
  // Exact 24×24 glyph paths (design_handoff_app_glyphs — the finalized round that
3
23
  // fixed racquetball, trips, and flow and added the guide mark). `color` only
4
24
  // matters for filled sub-shapes (e.g. the racquetball ball), which are filled
@@ -2,6 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { CircleUser, LogIn, LogOut, Settings } from "lucide-react";
4
4
  import { AppBadge } from "./AppBadge.js";
5
+ import { AppLogo, appGlyphForSlug } from "./AppLogo.js";
5
6
  import { SKIPLEAGUE_ACCOUNT_URL, SKIPLEAGUE_APPS } from "./apps.js";
6
7
  /**
7
8
  * The canonical SkipLeague account control: a boxed user-icon button that opens
@@ -56,6 +57,10 @@ export function ProfileMenu({ user, currentSlug, apps = SKIPLEAGUE_APPS, enabled
56
57
  display: "inline-flex",
57
58
  alignItems: "center",
58
59
  justifyContent: "center",
60
+ // Explicit padding:0 — a host app's global `button { padding }` would
61
+ // otherwise collapse this fixed 38×38 box's content area (border-box)
62
+ // and shrink the icon to a dot.
63
+ padding: 0,
59
64
  width: 38,
60
65
  height: 38,
61
66
  borderRadius: "var(--skl-radius-control)",
@@ -66,7 +71,9 @@ export function ProfileMenu({ user, currentSlug, apps = SKIPLEAGUE_APPS, enabled
66
71
  transition: "border-color 0.15s, color 0.15s",
67
72
  }, children: _jsx(CircleUser, { size: 22 }) }), open && (_jsx("div", { role: "menu", style: menuStyle, children: showSignedOut ? (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: "0.5rem 0.75rem", borderBottom: "1px solid var(--skl-color-border)" }, children: _jsx("div", { style: { fontWeight: 600, fontSize: "var(--skl-text-sm)", color: "var(--skl-color-text)" }, children: "Not signed in" }) }), _jsxs("button", { role: "menuitem", onClick: onSignIn, style: { ...itemStyle, width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer" }, children: [_jsx(LogIn, { size: 15 }), " ", signInLabel] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { style: { padding: "0.5rem 0.75rem", borderBottom: "1px solid var(--skl-color-border)" }, children: [_jsx("div", { style: { fontWeight: 600, fontSize: "var(--skl-text-sm)", color: "var(--skl-color-text)" }, children: label }), user?.email && _jsx("div", { style: { fontSize: "var(--skl-text-xs)", color: "var(--skl-color-text-muted)" }, children: user.email })] }), visibleApps.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { style: switchHeading, children: "Switch app" }), visibleApps.map((a) => {
68
73
  const isCurrent = a.slug === currentSlug;
69
- const inner = (_jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [_jsx(AppBadge, { name: a.name }), a.name] }));
74
+ // Per-app glyph when one exists; letter badge otherwise.
75
+ const glyph = appGlyphForSlug(a.slug);
76
+ const inner = (_jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [glyph ? _jsx(AppLogo, { app: glyph, size: 22 }) : _jsx(AppBadge, { name: a.name }), a.name] }));
70
77
  // The app you're in: light-green highlight, not clickable.
71
78
  return isCurrent ? (_jsx("div", { "aria-current": "page", style: { ...itemStyle, fontWeight: 600, background: "var(--skl-color-current-bg)", color: "var(--skl-color-current-text)", cursor: "default" }, children: inner }, a.slug)) : (_jsx("a", { href: a.url, role: "menuitem", style: itemStyle, children: inner }, a.slug));
72
79
  })] })), _jsx("div", { style: divider }), accountItem, _jsx("div", { style: divider }), _jsxs("button", { role: "menuitem", onClick: onSignOut, style: { ...itemStyle, width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer" }, children: [_jsx(LogOut, { size: 15 }), " Sign out"] })] })) }))] }));
package/dist/TopBar.js CHANGED
@@ -82,6 +82,9 @@ export function TopBarIconButton({ tone = "dark", compact = false, style, childr
82
82
  justifyContent: "center",
83
83
  cursor: "pointer",
84
84
  flex: "0 0 auto",
85
+ // Explicit padding:0 so a host app's global `button { padding }` can't
86
+ // collapse this fixed-size box (border-box) and shrink the icon.
87
+ padding: 0,
85
88
  background: palette.background,
86
89
  border: `1px solid ${palette.border}`,
87
90
  color: palette.color,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { ProfileMenu } from "./ProfileMenu.js";
2
2
  export type { ProfileMenuProps, ProfileMenuUser, ProfileMenuLinkArgs } from "./ProfileMenu.js";
3
3
  export { AppBadge } from "./AppBadge.js";
4
- export { AppLogo } from "./AppLogo.js";
4
+ export { AppLogo, APP_GLYPHS, appGlyphForSlug } from "./AppLogo.js";
5
5
  export type { AppGlyph } from "./AppLogo.js";
6
6
  export { TopBar, TopBarIconButton, DesktopActionBar } from "./TopBar.js";
7
7
  export type { TopBarTone } from "./TopBar.js";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ProfileMenu } from "./ProfileMenu.js";
2
2
  export { AppBadge } from "./AppBadge.js";
3
- export { AppLogo } from "./AppLogo.js";
3
+ export { AppLogo, APP_GLYPHS, appGlyphForSlug } from "./AppLogo.js";
4
4
  export { TopBar, TopBarIconButton, DesktopActionBar } from "./TopBar.js";
5
5
  export { SidebarNav } from "./SidebarNav.js";
6
6
  export { BottomNav } from "./BottomNav.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skipleague/design",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "SkipUI — the SkipLeague design system: shared tokens and React components for every SkipLeague app.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/AppLogo.tsx CHANGED
@@ -11,6 +11,28 @@ export type AppGlyph =
11
11
  | "guide"
12
12
  | "flow";
13
13
 
14
+ /** Every app that has a glyph, for slug→glyph resolution and fallbacks. */
15
+ export const APP_GLYPHS: readonly AppGlyph[] = [
16
+ "lists",
17
+ "racquetball",
18
+ "trips",
19
+ "gifts",
20
+ "reading",
21
+ "today",
22
+ "guide",
23
+ "flow",
24
+ ];
25
+
26
+ /**
27
+ * Resolve a platform app slug (e.g. `"skipracquetball"`) to its {@link AppGlyph}
28
+ * by dropping the `skip` prefix, or `null` when no glyph exists for it (so a
29
+ * caller can fall back to a letter {@link AppBadge}). Case-insensitive.
30
+ */
31
+ export function appGlyphForSlug(slug: string): AppGlyph | null {
32
+ const key = slug.replace(/^skip/i, "").toLowerCase();
33
+ return (APP_GLYPHS as readonly string[]).includes(key) ? (key as AppGlyph) : null;
34
+ }
35
+
14
36
  // Exact 24×24 glyph paths (design_handoff_app_glyphs — the finalized round that
15
37
  // fixed racquetball, trips, and flow and added the guide mark). `color` only
16
38
  // matters for filled sub-shapes (e.g. the racquetball ball), which are filled
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
2
2
  import { CircleUser, LogIn, LogOut, Settings } from "lucide-react";
3
3
  import { AppBadge } from "./AppBadge.js";
4
+ import { AppLogo, appGlyphForSlug } from "./AppLogo.js";
4
5
  import { SKIPLEAGUE_ACCOUNT_URL, SKIPLEAGUE_APPS, type AppLink } from "./apps.js";
5
6
 
6
7
  export interface ProfileMenuUser {
@@ -160,6 +161,10 @@ export function ProfileMenu({
160
161
  display: "inline-flex",
161
162
  alignItems: "center",
162
163
  justifyContent: "center",
164
+ // Explicit padding:0 — a host app's global `button { padding }` would
165
+ // otherwise collapse this fixed 38×38 box's content area (border-box)
166
+ // and shrink the icon to a dot.
167
+ padding: 0,
163
168
  width: 38,
164
169
  height: 38,
165
170
  borderRadius: "var(--skl-radius-control)",
@@ -202,9 +207,11 @@ export function ProfileMenu({
202
207
  <div style={switchHeading}>Switch app</div>
203
208
  {visibleApps.map((a) => {
204
209
  const isCurrent = a.slug === currentSlug;
210
+ // Per-app glyph when one exists; letter badge otherwise.
211
+ const glyph = appGlyphForSlug(a.slug);
205
212
  const inner = (
206
213
  <span style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
207
- <AppBadge name={a.name} />
214
+ {glyph ? <AppLogo app={glyph} size={22} /> : <AppBadge name={a.name} />}
208
215
  {a.name}
209
216
  </span>
210
217
  );
package/src/TopBar.tsx CHANGED
@@ -168,6 +168,9 @@ export function TopBarIconButton({
168
168
  justifyContent: "center",
169
169
  cursor: "pointer",
170
170
  flex: "0 0 auto",
171
+ // Explicit padding:0 so a host app's global `button { padding }` can't
172
+ // collapse this fixed-size box (border-box) and shrink the icon.
173
+ padding: 0,
171
174
  background: palette.background,
172
175
  border: `1px solid ${palette.border}`,
173
176
  color: palette.color,
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { ProfileMenu } from "./ProfileMenu.js";
2
2
  export type { ProfileMenuProps, ProfileMenuUser, ProfileMenuLinkArgs } from "./ProfileMenu.js";
3
3
  export { AppBadge } from "./AppBadge.js";
4
- export { AppLogo } from "./AppLogo.js";
4
+ export { AppLogo, APP_GLYPHS, appGlyphForSlug } from "./AppLogo.js";
5
5
  export type { AppGlyph } from "./AppLogo.js";
6
6
  export { TopBar, TopBarIconButton, DesktopActionBar } from "./TopBar.js";
7
7
  export type { TopBarTone } from "./TopBar.js";