@tanishqkancharla/maui 0.0.57 → 0.0.59

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.
@@ -20,6 +20,7 @@ const disabledRaised = {
20
20
  opacity: 1,
21
21
  color: colors.gray[8],
22
22
  backgroundColor: backgroundColor.element,
23
+ backgroundImage: "none",
23
24
  boxShadow: buttonShadow,
24
25
  };
25
26
  const disabledQuiet = {
@@ -141,40 +142,48 @@ function darkerFill(color) {
141
142
  function onSolidText(color) {
142
143
  return `oklch(from ${color} clamp(0.2, (0.75 - l) * 100, 0.99) 0 0)`;
143
144
  }
145
+ /** Darker shade of a fill (OKLCH lightness offset) at an alpha. */
146
+ function fillShade(color, lightnessOffset, alpha = 1) {
147
+ return `oklch(from ${color} calc(l - ${lightnessOffset}) c h / ${alpha})`;
148
+ }
149
+ /** Fill-tinted edge and lift; no neutral gray, so the shadow matches any fill. */
144
150
  function primaryButtonShadow(color) {
145
- return `inset 0 1px #ffffff18, inset 0 0 1px .5px #fff2, 0 1px .5px #0003, 0 0 3px -1px color-mix(in oklch, ${color}, black 35%)`;
151
+ return `inset 0 1px #ffffff18, inset 0 0 1px .5px #fff2, 0 0 0 .5px ${fillShade(color, 0.1)}, 0 1px 2px ${fillShade(color, 0, 0.35)}, 0 3px 8px -3px ${fillShade(color, 0, 0.6)}`;
146
152
  }
153
+ /**
154
+ * Diagonal from a slightly darker, more saturated, warmer-rotated fill to the
155
+ * fill. The offsets are the OKLCH delta between Campsite's #FB432C → #FF591E.
156
+ */
157
+ function primaryButtonGradient(color) {
158
+ return `linear-gradient(to top left, oklch(from ${color} calc(l - 0.027) calc(c * 1.05) calc(h - 6.8)), ${color})`;
159
+ }
160
+ /** Hover lightens the top of the gradient instead of replacing the fill. */
161
+ const primaryHoverSheen = `linear-gradient(to bottom, rgb(255 255 255 / 0.12), rgb(255 255 255 / 0))`;
162
+ const primaryClass = memoize((fill, textColor) => {
163
+ const primaryShadow = primaryButtonShadow(fill);
164
+ const gradient = primaryButtonGradient(fill);
165
+ return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
166
+ color: textColor,
167
+ backgroundColor: fill,
168
+ backgroundImage: gradient,
169
+ boxShadow: primaryShadow,
170
+ "&:hover:not(:disabled)": {
171
+ backgroundImage: `${primaryHoverSheen}, ${gradient}`,
172
+ },
173
+ "&:disabled": disabledRaised,
174
+ });
175
+ });
147
176
  const coloredButtonClass = memoize((variant, color) => {
148
177
  if (isCssColor(color)) {
149
178
  const fill = opaqueColor(color);
150
179
  if (variant === "primary") {
151
- const hover = surfaceWash(colors.gray[12], surfaceMixPercent.hover, fill);
152
- const primaryShadow = primaryButtonShadow(fill);
153
- return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
154
- color: onSolidText(fill),
155
- backgroundColor: fill,
156
- boxShadow: primaryShadow,
157
- "&:hover:not(:disabled)": {
158
- backgroundColor: hover,
159
- },
160
- "&:disabled": disabledRaised,
161
- });
180
+ return primaryClass(fill, onSolidText(fill));
162
181
  }
163
182
  return quietClass(fill, darkerFill(fill), fill);
164
183
  }
165
184
  const scale = paletteFill(color);
166
185
  if (variant === "primary") {
167
- const primaryShadow = primaryButtonShadow(scale[9]);
168
- const hover = surfaceWash(colors.gray[12], surfaceMixPercent.hover, scale[9]);
169
- return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
170
- color: darkTextOnSolid.has(color) ? scale[12] : "white",
171
- backgroundColor: scale[9],
172
- boxShadow: primaryShadow,
173
- "&:hover:not(:disabled)": {
174
- backgroundColor: hover,
175
- },
176
- "&:disabled": disabledRaised,
177
- });
186
+ return primaryClass(scale[9], darkTextOnSolid.has(color) ? scale[12] : "white");
178
187
  }
179
188
  return quietClass(scale[11], scale[12], paletteAlpha(color)[9]);
180
189
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanishqkancharla/maui",
3
- "version": "0.0.57",
3
+ "version": "0.0.59",
4
4
  "description": "Maui design system",
5
5
  "type": "module",
6
6
  "main": "./dist/maui.js",
@@ -30,7 +30,7 @@ Styled React Aria `Button` (default `type="button"`). Consumes `ButtonContext`,
30
30
  | `onClick` | Still supported. React Aria also exposes `onPress` |
31
31
  | `children` | Text is wrapped for cap-height trim; SVG icons sit beside text. Icon-only needs `aria-label` |
32
32
 
33
- Primary: fill step 9, hover 10, light text (`onAccent`, or step 12 on amber/lime/mint/sky/yellow). Custom CSS fill drops alpha; hover is `l - 0.04`; text is white or near-black from lightness. Its raised shadow strengthens the white inset highlight and tints the tight outer edge from the fill. Quiet: no fill; `variantColor` tints the label/icon. Hover/press mix `grayAlpha[9]` (or that color’s alpha 9, or the hex) into transparent at 6% light / 9% dark; press is 2×. Hover fills snap.
33
+ Primary: fill step 9, light text (`onAccent`, or step 12 on amber/lime/mint/sky/yellow). Custom CSS fill drops alpha; text is white or near-black from lightness. The fill is a diagonal gradient (`to top left`) from the fill shifted by OKLCH `l − 0.027`, `c × 1.05`, `h − 6.8` to the fill itself. The shadow keeps the white inset highlight and tints a 0.5px edge (`l − 0.1`) and a soft lift from the fill — no neutral gray. Hover layers a white-to-transparent sheen over the gradient; disabled drops the gradient. Quiet: no fill; `variantColor` tints the label/icon. Hover/press mix `grayAlpha[9]` (or that color’s alpha 9, or the hex) into transparent at 6% light / 9% dark; press is 2×. Hover fills snap.
34
34
 
35
35
  While a menu or overlay is open, the trigger keeps the active fill via RAC `data-pressed` / `aria-expanded` (same tokens as `:active`: `elementActive`, or primary step 10).
36
36
 
@@ -31,6 +31,7 @@ const disabledRaised = {
31
31
  opacity: 1,
32
32
  color: colors.gray[8],
33
33
  backgroundColor: backgroundColor.element,
34
+ backgroundImage: "none",
34
35
  boxShadow: buttonShadow,
35
36
  } as const
36
37
 
@@ -187,34 +188,48 @@ function onSolidText(color: string) {
187
188
  return `oklch(from ${color} clamp(0.2, (0.75 - l) * 100, 0.99) 0 0)`
188
189
  }
189
190
 
191
+ /** Darker shade of a fill (OKLCH lightness offset) at an alpha. */
192
+ function fillShade(color: string, lightnessOffset: number, alpha = 1) {
193
+ return `oklch(from ${color} calc(l - ${lightnessOffset}) c h / ${alpha})`
194
+ }
195
+
196
+ /** Fill-tinted edge and lift; no neutral gray, so the shadow matches any fill. */
190
197
  function primaryButtonShadow(color: string) {
191
- return `inset 0 1px #ffffff18, inset 0 0 1px .5px #fff2, 0 1px .5px #0003, 0 0 3px -1px color-mix(in oklch, ${color}, black 35%)`
198
+ return `inset 0 1px #ffffff18, inset 0 0 1px .5px #fff2, 0 0 0 .5px ${fillShade(color, 0.1)}, 0 1px 2px ${fillShade(color, 0, 0.35)}, 0 3px 8px -3px ${fillShade(color, 0, 0.6)}`
199
+ }
200
+
201
+ /**
202
+ * Diagonal from a slightly darker, more saturated, warmer-rotated fill to the
203
+ * fill. The offsets are the OKLCH delta between Campsite's #FB432C → #FF591E.
204
+ */
205
+ function primaryButtonGradient(color: string) {
206
+ return `linear-gradient(to top left, oklch(from ${color} calc(l - 0.027) calc(c * 1.05) calc(h - 6.8)), ${color})`
192
207
  }
193
208
 
209
+ /** Hover lightens the top of the gradient instead of replacing the fill. */
210
+ const primaryHoverSheen = `linear-gradient(to bottom, rgb(255 255 255 / 0.12), rgb(255 255 255 / 0))`
211
+
212
+ const primaryClass = memoize((fill: string, textColor: string) => {
213
+ const primaryShadow = primaryButtonShadow(fill)
214
+ const gradient = primaryButtonGradient(fill)
215
+ return style(buttonBaseClass, focusRing("&:focus-visible", primaryShadow), {
216
+ color: textColor,
217
+ backgroundColor: fill,
218
+ backgroundImage: gradient,
219
+ boxShadow: primaryShadow,
220
+ "&:hover:not(:disabled)": {
221
+ backgroundImage: `${primaryHoverSheen}, ${gradient}`,
222
+ },
223
+ "&:disabled": disabledRaised,
224
+ })
225
+ })
226
+
194
227
  const coloredButtonClass = memoize(
195
228
  (variant: "primary" | "quiet", color: ButtonVariantColor) => {
196
229
  if (isCssColor(color)) {
197
230
  const fill = opaqueColor(color)
198
231
  if (variant === "primary") {
199
- const hover = surfaceWash(
200
- colors.gray[12],
201
- surfaceMixPercent.hover,
202
- fill,
203
- )
204
- const primaryShadow = primaryButtonShadow(fill)
205
- return style(
206
- buttonBaseClass,
207
- focusRing("&:focus-visible", primaryShadow),
208
- {
209
- color: onSolidText(fill),
210
- backgroundColor: fill,
211
- boxShadow: primaryShadow,
212
- "&:hover:not(:disabled)": {
213
- backgroundColor: hover,
214
- },
215
- "&:disabled": disabledRaised,
216
- },
217
- )
232
+ return primaryClass(fill, onSolidText(fill))
218
233
  }
219
234
 
220
235
  return quietClass(fill, darkerFill(fill), fill)
@@ -222,24 +237,9 @@ const coloredButtonClass = memoize(
222
237
 
223
238
  const scale = paletteFill(color)
224
239
  if (variant === "primary") {
225
- const primaryShadow = primaryButtonShadow(scale[9])
226
- const hover = surfaceWash(
227
- colors.gray[12],
228
- surfaceMixPercent.hover,
240
+ return primaryClass(
229
241
  scale[9],
230
- )
231
- return style(
232
- buttonBaseClass,
233
- focusRing("&:focus-visible", primaryShadow),
234
- {
235
- color: darkTextOnSolid.has(color) ? scale[12] : "white",
236
- backgroundColor: scale[9],
237
- boxShadow: primaryShadow,
238
- "&:hover:not(:disabled)": {
239
- backgroundColor: hover,
240
- },
241
- "&:disabled": disabledRaised,
242
- },
242
+ darkTextOnSolid.has(color) ? scale[12] : "white",
243
243
  )
244
244
  }
245
245
 
@@ -85,8 +85,9 @@ export function ButtonsPage() {
85
85
  <Code>variant="primary"</Code> fills with step 9 of{" "}
86
86
  <Code>variantColor</Code> (a palette name, default <Code>"accent"</Code>
87
87
  ). Pass a hex or <Code>rgb()</Code> string to use that color as the fill
88
- (alpha is dropped). Primary buttons use a stronger inset highlight and a
89
- tight edge tinted from the fill.
88
+ (alpha is dropped). The fill is a subtle diagonal gradient from a
89
+ slightly darker, warmer shade of that color, and the edge and lift
90
+ shadows are tinted from it. Hover adds a white sheen at the top.
90
91
  </P>
91
92
  <Flex row alignItems="center" gap={4} style={{ flexWrap: "wrap" }}>
92
93
  <Button variant="primary">Save</Button>
@@ -27,7 +27,7 @@ import { type ThemePreference, useTheme } from "../theme/ThemeContext"
27
27
  import { isScalePreference, useScale } from "../theme/ScaleContext"
28
28
  import { LARGE_SCALE } from "../theme/dataScale"
29
29
  import { galleryCompactMedia } from "./galleryCompact"
30
- import { AboutPage } from "./AboutPage"
30
+ import { ReadmePage } from "./ReadmePage"
31
31
  import { AvatarPage } from "./AvatarPage"
32
32
  import { BadgePage } from "./BadgePage"
33
33
  import { BackgroundColorTokenPage } from "./BackgroundColorTokenPage"
@@ -106,7 +106,7 @@ function navItems(entries: NavEntry[]): NavItem[] {
106
106
  }
107
107
 
108
108
  const navigation: NavEntry[] = [
109
- { label: "About", path: "/about", page: AboutPage },
109
+ { label: "README", path: "/readme", page: ReadmePage },
110
110
  { label: "Editor", path: "/editor", page: JsxEditorPage },
111
111
  {
112
112
  label: "Tokens",
@@ -241,7 +241,7 @@ const navigation: NavEntry[] = [
241
241
  },
242
242
  ]
243
243
 
244
- const defaultPath = "/about"
244
+ const defaultPath = "/readme"
245
245
 
246
246
  function useGalleryCompact() {
247
247
  const [compact, setCompact] = useState(() =>
@@ -317,6 +317,9 @@ function MauiContent() {
317
317
  </Route>
318
318
  ))}
319
319
 
320
+ <Route path="/about">
321
+ <Redirect to="/readme" />
322
+ </Route>
320
323
  <Route path="/patterns/calendar">
321
324
  <Redirect to="/apps/calendar" />
322
325
  </Route>
@@ -0,0 +1,83 @@
1
+ import { Code } from "../components/Code"
2
+ import { CodeBlock } from "../components/CodeBlock"
3
+ import { Prose } from "../components/Prose"
4
+ import { H2, H3, Li, Link, P, Ul } from "../components/Typography"
5
+
6
+ export function ReadmePage() {
7
+ return (
8
+ <Prose style={{ marginBottom: "32px" }}>
9
+ <H2>Maui</H2>
10
+ <P>
11
+ Maui is an open-source, TypeScript-first design system styled with{" "}
12
+ <Link href="https://www.npmjs.com/package/purse-styles">
13
+ purse-styles
14
+ </Link>
15
+ . It is developed as part of{" "}
16
+ <Link href="https://gethalo.dev">Halo</Link>. This site is a gallery of
17
+ its tokens, components, patterns, and demo apps.
18
+ </P>
19
+ <Ul>
20
+ <Li>
21
+ Source:{" "}
22
+ <Link href="https://github.com/tanishqkancharla/maui">
23
+ github.com/tanishqkancharla/maui
24
+ </Link>
25
+ </Li>
26
+ <Li>
27
+ Package:{" "}
28
+ <Link href="https://www.npmjs.com/package/@tanishqkancharla/maui">
29
+ @tanishqkancharla/maui
30
+ </Link>
31
+ </Li>
32
+ </Ul>
33
+
34
+ <H3>Install</H3>
35
+ <CodeBlock lang="sh">{`npm install @tanishqkancharla/maui motion`}</CodeBlock>
36
+ <P>
37
+ <Code>motion</Code> is a required peer dependency, used by components
38
+ with animated presence and transitions. The package ships compiled
39
+ JavaScript and type declarations.
40
+ </P>
41
+
42
+ <H3>Usage</H3>
43
+ <P>
44
+ Wrap your app in <Code>MauiProvider</Code>, then import tokens and
45
+ components:
46
+ </P>
47
+ <CodeBlock lang="tsx">{`import { MauiProvider, Button } from "@tanishqkancharla/maui"
48
+
49
+ function App() {
50
+ return (
51
+ <MauiProvider>
52
+ <Button>Hello</Button>
53
+ </MauiProvider>
54
+ )
55
+ }`}</CodeBlock>
56
+ <P>
57
+ Put the exported <Code>themeFoucScript</Code> in an inline{" "}
58
+ <Code>{"<script>"}</Code> in <Code>{"<head>"}</Code> so the theme is
59
+ correct on first paint.
60
+ </P>
61
+
62
+ <H3>Develop</H3>
63
+ <CodeBlock lang="sh">{`npm install
64
+ npm run dev`}</CodeBlock>
65
+ <P>
66
+ Vite serves this gallery locally. Patterns and demo apps are reference
67
+ implementations, not package components.
68
+ </P>
69
+
70
+ <H3>License</H3>
71
+ <P>
72
+ Maui is available under the{" "}
73
+ <Link href="https://github.com/tanishqkancharla/maui/blob/main/LICENSE">
74
+ MIT License
75
+ </Link>
76
+ .
77
+ </P>
78
+
79
+ <H3>Contact</H3>
80
+ <P>For any questions, email tanishqkancharla3@gmail.com.</P>
81
+ </Prose>
82
+ )
83
+ }
@@ -1,19 +0,0 @@
1
- import { Prose } from "../components/Prose"
2
- import { H2, Link, P } from "../components/Typography"
3
-
4
- export function AboutPage() {
5
- return (
6
- <Prose>
7
- <H2>About</H2>
8
- <P>
9
- Maui is an open-source design system. It is developed as part of{" "}
10
- <Link href="https://gethalo.dev">Halo</Link>. It is available under the{" "}
11
- <Link href="https://github.com/tanishqkancharla/maui/blob/main/LICENSE">
12
- MIT License
13
- </Link>
14
- .
15
- </P>
16
- <P>For any questions, email tanishqkancharla3@gmail.com.</P>
17
- </Prose>
18
- )
19
- }