Skip to content

Commit 582bec9

Browse files
authored
feat(theme): add --spacing-em token for font-relative sizing (#1076)
* feat(theme): add --spacing-em token for font-relative sizing Add a `--spacing-em: 1em` theme token enabling Tailwind utilities like `size-em`, `w-em`, `h-em`, `p-em`, and `gap-em`. Register the custom spacing value in tailwind-merge so `cx` correctly resolves conflicts. * smol ngrok * address pr comments
1 parent d2c19d7 commit 582bec9

File tree

8 files changed

+40
-12
lines changed

8 files changed

+40
-12
lines changed

.changeset/add-spacing-em.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ngrok/mantle": patch
3+
---
4+
5+
Add `--spacing-em` (`1em`) theme token for font-relative sizing utilities like `size-em`, `w-em`, `h-em`

.changeset/some-ears-drop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ngrok/mantle": patch
3+
---
4+
5+
Add default `width="2.61em" height="1em"` to `NgrokWordmarkIcon` and `width="1em" height="1em"` to `NgrokLettermarkIcon` so they render at font size without requiring explicit sizing classes

packages/mantle-vite-plugins/src/mantle-tw-source-plugin.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "node:fs";
22
import os from "node:os";
33
import path from "node:path";
4-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
4+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
55
import { build } from "vite";
66
import { MARKER_END, MARKER_START } from "./internals.js";
77
import { mantleTwSourcePlugin } from "./mantle-tw-source-plugin.js";
@@ -106,6 +106,8 @@ describe("mantleTwSourcePlugin — input filtering", () => {
106106
}, 30_000);
107107

108108
it("filters invalid allowlist entries and only emits known components", async () => {
109+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
110+
109111
const css = await buildWith("export {};", {
110112
allowlist: [
111113
"button", // valid
@@ -119,6 +121,8 @@ describe("mantleTwSourcePlugin — input filtering", () => {
119121
expect(css).not.toContain("not-a-real-component");
120122
expect(css).not.toContain("evil");
121123
expect(css).not.toContain("cx.js");
124+
125+
warnSpy.mockRestore();
122126
}, 30_000);
123127

124128
it("emits only real components when source mixes valid and invalid imports", async () => {

packages/mantle/src/components/icons/ngrok.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import type { InlineIconProps } from "./types.js";
55
*/
66
function NgrokWordmarkIcon(props: Omit<InlineIconProps, "color">) {
77
return (
8-
<svg viewBox="0 0 94 36" fill="currentColor" {...props}>
9-
<path d="M32.272 12.011c-1.298-1.466-2.904-2.205-4.812-2.205-1.176 0-2.26.233-3.255.7a7.995 7.995 0 0 0-2.581 1.906 9.205 9.205 0 0 0-1.715 2.853 9.773 9.773 0 0 0-.628 3.546c0 1.25.194 2.39.58 3.419.362.98.918 1.877 1.635 2.636A7.543 7.543 0 0 0 24 26.584c.965.41 2.025.617 3.176.617.522 0 1.005-.041 1.445-.116.439-.075.858-.2 1.26-.37.4-.175.79-.398 1.18-.664.385-.27.792-.612 1.21-1.018v4.353h-.005v.421h-5.33l-4.005 4.64v.798h15.037v-24.98h-5.697v1.746Zm-.014 7.979a4.25 4.25 0 0 1-.786 1.215 3.555 3.555 0 0 1-2.592 1.1 3.627 3.627 0 0 1-1.464-.292 3.508 3.508 0 0 1-1.166-.808 3.93 3.93 0 0 1-1.054-2.72c0-.519.097-1.006.298-1.457a3.77 3.77 0 0 1 .804-1.181 4.114 4.114 0 0 1 1.162-.808 3.484 3.484 0 0 1 2.817-.016c.448.19.844.463 1.181.81.336.347.6.743.804 1.194.202.452.298.95.298 1.493 0 .505-.104 1.005-.302 1.47Zm-16.261-7.708a6.173 6.173 0 0 0-2.06-1.602 4.875 4.875 0 0 0-.57-.22 6.383 6.383 0 0 0-.923-.216H8.383L5.697 13.39v-3.082H.002v16.61h5.695V15.712h5.35l.444-.01v11.214h5.697V16.528c0-.885-.084-1.674-.25-2.366a4.655 4.655 0 0 0-.941-1.877v-.003Zm38.367-2.018h-6.213l-2.47 2.863v-2.864h-5.7v16.61h5.71l.004-11.117h4.144l4.526-5.26-.001-.232Zm31.051 7.672 7.79-7.392v-.281H85.7l-5.975 5.991V0h-5.696v26.87h5.696v-6.766l6.262 6.763h7.663v-.316l-8.233-8.617-.002.002Zm-16.11-5.78a9.436 9.436 0 0 0-3.085-1.842 10.953 10.953 0 0 0-3.855-.664c-1.407 0-2.705.226-3.884.678a9.611 9.611 0 0 0-3.072 1.858 8.488 8.488 0 0 0-2.016 2.788 8.281 8.281 0 0 0-.722 3.449c0 1.362.24 2.596.722 3.707a8.52 8.52 0 0 0 2.002 2.862c.85.798 1.86 1.415 3.036 1.847 1.177.432 2.455.647 3.842.647 1.406 0 2.707-.215 3.919-.647 1.204-.431 2.24-1.04 3.098-1.833a8.583 8.583 0 0 0 2.031-2.816c.493-1.09.742-2.29.742-3.611 0-1.316-.244-2.52-.722-3.612a8.424 8.424 0 0 0-2.035-2.81Zm-3.558 7.864c-.2.461-.463.869-.786 1.215a3.573 3.573 0 0 1-2.592 1.1c-.502 0-.981-.096-1.434-.291a3.44 3.44 0 0 1-1.16-.809 4.155 4.155 0 0 1-.788-1.215 3.825 3.825 0 0 1-.297-1.537c0-.517.098-1.004.297-1.456.201-.451.46-.849.787-1.194a3.579 3.579 0 0 1 2.597-1.1c.502 0 .98.096 1.43.29.448.19.839.461 1.16.81.328.345.586.752.786 1.214.2.461.297.954.297 1.471 0 .538-.096 1.04-.297 1.502Z" />
8+
<svg fill="currentColor" height="1em" viewBox="0 0 94 36" width="2.61em" {...props}>
9+
<path d="M32.272 12.011c-1.298-1.466-2.904-2.205-4.812-2.205-1.176 0-2.26.233-3.255.7a7.995 7.995 0 0 0-2.581 1.906 9.205 9.205 0 0 0-1.715 2.853 9.773 9.773 0 0 0-.628 3.546c0 1.25.194 2.39.58 3.419.362.98.918 1.877 1.635 2.636A7.543 7.543 0 0 0 24 26.584c.965.41 2.025.617 3.176.617.522 0 1.005-.041 1.445-.116.439-.075.858-.2 1.26-.37.4-.175.79-.398 1.18-.664.385-.27.792-.612 1.21-1.018v4.353h-.005v.421h-5.33l-4.005 4.64v.798h15.037v-24.98h-5.697v1.746Zm-.014 7.979a4.25 4.25 0 0 1-.786 1.215 3.555 3.555 0 0 1-2.592 1.1 3.627 3.627 0 0 1-1.464-.292 3.508 3.508 0 0 1-1.166-.808 3.93 3.93 0 0 1-1.054-2.72c0-.519.097-1.006.298-1.457a3.77 3.77 0 0 1 .804-1.181 4.114 4.114 0 0 1 1.162-.808 3.484 3.484 0 0 1 2.817-.016c.448.19.844.463 1.181.81.336.347.6.743.804 1.194.202.452.298.95.298 1.493 0 .505-.104 1.005-.302 1.47m-16.261-7.708a6.173 6.173 0 0 0-2.06-1.602 4.875 4.875 0 0 0-.57-.22 6.383 6.383 0 0 0-.923-.216H8.383L5.697 13.39v-3.082H.002v16.61h5.695V15.712h5.35l.444-.01v11.214h5.697V16.528c0-.885-.084-1.674-.25-2.366a4.655 4.655 0 0 0-.941-1.877zm38.367-2.018h-6.213l-2.47 2.863v-2.864h-5.7v16.61h5.71l.004-11.117h4.144l4.526-5.26zm31.051 7.672 7.79-7.392v-.281H85.7l-5.975 5.991V0h-5.696v26.87h5.696v-6.766l6.262 6.763h7.663v-.316l-8.233-8.617zm-16.11-5.78a9.436 9.436 0 0 0-3.085-1.842 10.953 10.953 0 0 0-3.855-.664c-1.407 0-2.705.226-3.884.678a9.611 9.611 0 0 0-3.072 1.858 8.488 8.488 0 0 0-2.016 2.788 8.281 8.281 0 0 0-.722 3.449c0 1.362.24 2.596.722 3.707a8.52 8.52 0 0 0 2.002 2.862c.85.798 1.86 1.415 3.036 1.847 1.177.432 2.455.647 3.842.647 1.406 0 2.707-.215 3.919-.647 1.204-.431 2.24-1.04 3.098-1.833a8.583 8.583 0 0 0 2.031-2.816c.493-1.09.742-2.29.742-3.611 0-1.316-.244-2.52-.722-3.612a8.424 8.424 0 0 0-2.035-2.81Zm-3.558 7.864c-.2.461-.463.869-.786 1.215a3.573 3.573 0 0 1-2.592 1.1c-.502 0-.981-.096-1.434-.291a3.44 3.44 0 0 1-1.16-.809 4.155 4.155 0 0 1-.788-1.215 3.825 3.825 0 0 1-.297-1.537c0-.517.098-1.004.297-1.456.201-.451.46-.849.787-1.194a3.579 3.579 0 0 1 2.597-1.1c.502 0 .98.096 1.43.29.448.19.839.461 1.16.81.328.345.586.752.786 1.214.2.461.297.954.297 1.471 0 .538-.096 1.04-.297 1.502" />
1010
</svg>
1111
);
1212
}
@@ -16,7 +16,7 @@ function NgrokWordmarkIcon(props: Omit<InlineIconProps, "color">) {
1616
*/
1717
function NgrokLettermarkIcon(props: Omit<InlineIconProps, "color">) {
1818
return (
19-
<svg viewBox="0 0 32 32" fill="currentColor" {...props}>
19+
<svg fill="currentColor" height="1em" viewBox="0 0 32 32" width="1em" {...props}>
2020
<path d="M27.2 6.18a9.47 9.47 0 0 0-3.12-2.5A9.42 9.42 0 0 0 21.82 3h-6.14l-4.06 4.9V3.1H3V29h8.62V11.53h8.09l.67-.02v17.48H29V12.8c0-1.37-.13-2.6-.38-3.68a7.35 7.35 0 0 0-1.42-2.93Z" />
2121
</svg>
2222
);

packages/mantle/src/components/icons/traffic-policy-file.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,7 @@ import type { SvgAttributes } from "../icon/types.js";
55
*/
66
function TrafficPolicyFileIcon(props: SvgAttributes) {
77
return (
8-
<svg
9-
xmlns="http://www.w3.org/2000/svg"
10-
width="1em"
11-
height="1em"
12-
fill="currentColor"
13-
viewBox="0 0 256 256"
14-
{...props}
15-
>
8+
<svg fill="currentColor" height="1em" viewBox="0 0 256 256" width="1em" {...props}>
169
<path fill="none" d="M0 0h256v256H0z" />
1710
<path d="m213.7 82.3-56-56c-1.5-1.5-3.5-2.3-5.7-2.3H56c-8.8 0-16 7.2-16 16v88c0 4.4 3.6 8 8 8s8-3.6 8-8V40h88v48c0 4.4 3.6 8 8 8h48v120h-40c-4.4 0-8 3.6-8 8s3.6 8 8 8h40c8.8 0 16-7.2 16-16V88c0-2.1-.8-4.2-2.3-5.7zm-53.7-31L188.7 80H160V51.3z" />
1811
<path d="M124.6 194.5h-6.8v-27.3h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4h-6.8v-10.2c0-3.8-3.1-6.8-6.8-6.8H63.3c-3.8 0-6.8 3.1-6.8 6.8v10.2h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v27.3h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v23.9c0 3.8 3.1 6.8 6.8 6.8H111c3.8 0 6.8-3.1 6.8-6.8v-23.9h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4zm-37.5-11.9c-6.6 0-11.9-5.3-11.9-11.9s5.3-11.9 11.9-11.9S99 164.1 99 170.7s-5.3 11.9-11.9 11.9zm0 10.2c6.6 0 11.9 5.3 11.9 11.9s-5.3 11.9-11.9 11.9-11.9-5.3-11.9-11.9 5.3-11.9 11.9-11.9z" />

packages/mantle/src/mantle.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ MARK: THEME
151151
0px 11px 10px 0px --alpha(var(--shadow-color) / var(--shadow-first-opacity)),
152152
0px 17px 25px 2px --alpha(var(--shadow-color) / var(--shadow-second-opacity));
153153
--shadow-inner: inset 0px 2px 4px 0px --alpha(var(--shadow-color) / var(--shadow-first-opacity));
154+
155+
--spacing-em: 1em;
154156
}
155157

156158
@theme inline {

packages/mantle/src/utils/cx/cx.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,20 @@ describe("cx", () => {
6868
test("text-base can be overridden by text-size-inherit", () => {
6969
expect(cx("text-base text-size-inherit")).toBe("text-size-inherit");
7070
});
71+
72+
test("w-4 can be overridden by w-em", () => {
73+
expect(cx("w-4 w-em")).toBe("w-em");
74+
});
75+
76+
test("h-em can be overridden by h-6", () => {
77+
expect(cx("h-em h-6")).toBe("h-6");
78+
});
79+
80+
test("p-em can be overridden by p-2", () => {
81+
expect(cx("p-em p-2")).toBe("p-2");
82+
});
83+
84+
test("gap-4 can be overridden by gap-em", () => {
85+
expect(cx("gap-4 gap-em")).toBe("gap-em");
86+
});
7187
});

packages/mantle/src/utils/cx/cx.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const twMerge = extendTailwindMerge({
66
classGroups: {
77
"font-size": ["text-mono", "text-size-inherit"],
88
},
9+
theme: {
10+
spacing: ["em"],
11+
},
912
},
1013
});
1114

0 commit comments

Comments
 (0)