Skip to content

Commit ae73b04

Browse files
committed
Merge branch 'main' into fix-font-loading
2 parents d0d0145 + 0555f15 commit ae73b04

File tree

15 files changed

+902
-37
lines changed

15 files changed

+902
-37
lines changed
Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,48 @@
1+
import { PropsTable } from '@/components/PropsTable'
2+
13
###### API
24

35
`Button` props extends the button HTML attributes.
46

5-
<div style={{ width: '100%', overflow: 'auto'}}>
6-
| Property | Description | Type | Default |
7-
| --- | --- | --- | --- |
8-
| **variant** | The variant of the button | `'primary' \| 'default'` | `'default'` |
9-
| **colors** | The color variables of the button, i.e. `var(--primary)` | ```{<br> primary?: string<br> error?: string<br> text?: string<br> border?: string<br> inputBackground?: string<br> primaryFocus?: string<br>}``` | `undefined` |
10-
| **danger** | Signals that it should be used with caution. It is often used in a delete button or to show the error status. | `boolean` | `false` |
11-
| **size** | The size of the button | `'sm' \| 'md' \| 'lg'` | `'md'` |
12-
| **icon** | Icon of the button passed in as a form of ReactNode | `React.ReactNode` | `undefined` |
13-
| **ellipsis** | Whether the button text should be truncated with an ellipsis. The button should have a width to be able to truncate the text. | `boolean` | `false` |
14-
</div>
7+
<PropsTable
8+
componentProps={[
9+
{
10+
property: 'variant',
11+
description: 'The variant of the button',
12+
type: "`'primary' | 'default'`",
13+
default: "`'default'`",
14+
},
15+
{
16+
property: 'colors',
17+
description: 'The color variables of the button, i.e. `var(--primary)`',
18+
type: '```{<br> primary?: string<br> error?: string<br> text?: string<br> border?: string<br> inputBackground?: string<br> primaryFocus?: string<br>}```',
19+
default: '`undefined`',
20+
},
21+
{
22+
property: 'danger',
23+
description:
24+
'Signals that it should be used with caution. It is often used in a delete button or to show the error status.',
25+
type: '`boolean`',
26+
default: '`false`',
27+
},
28+
{
29+
property: 'size',
30+
description: 'The size of the button',
31+
type: "`'sm' | 'md' | 'lg'`",
32+
default: "`'md'`",
33+
},
34+
{
35+
property: 'icon',
36+
description: 'Icon of the button passed in as a form of ReactNode',
37+
type: '`React.ReactNode`',
38+
default: '`undefined`',
39+
},
40+
{
41+
property: 'ellipsis',
42+
description:
43+
'Whether the button text should be truncated with an ellipsis. The button should have a width to be able to truncate the text.',
44+
type: '`boolean`',
45+
default: '`false`',
46+
},
47+
]}
48+
/>

apps/landing/src/app/(detail)/components/[component]/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { CustomH6 } from '@/components/mdx/components/CustomH6'
66
import { CustomParagraph } from '@/components/mdx/components/CustomParagraph'
77
import { CustomPre } from '@/components/mdx/components/CustomPre'
88
import { CustomStrong } from '@/components/mdx/components/CustomStrong'
9+
import { PropsTable } from '@/components/PropsTable'
910
import { COMPONENT_GROUPS } from '@/constants'
1011
import { getDemos } from '@/utils/get-demos'
1112

@@ -102,6 +103,7 @@ export default async function Page({
102103
h6: CustomH6,
103104
pre: CustomPre,
104105
p: CustomParagraph,
106+
PropsTable,
105107
}}
106108
/>
107109
</VStack>

apps/landing/src/app/(detail)/docs/LeftMenu.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,37 @@ export function LeftMenu() {
1818
to: '/docs/core-concepts/style-storage',
1919
children: 'Style Storage',
2020
},
21+
{
22+
to: '/docs/core-concepts/type-inference-system',
23+
children: 'Type Inference System',
24+
},
25+
{
26+
to: '/docs/core-concepts/optimize-css',
27+
children: 'Optimize CSS',
28+
},
29+
{
30+
to: '/docs/core-concepts/nm-base',
31+
children: 'N/M Base',
32+
},
2133
]}
2234
>
2335
Core Concepts
2436
</MenuItem>
2537
<MenuItem to="/docs/features">Features</MenuItem>
38+
<MenuItem
39+
subMenu={[
40+
{
41+
to: '/docs/figma-and-theme-integration/devup-figma-plugin',
42+
children: 'Devup Figma Plugin',
43+
},
44+
{
45+
to: '/docs/figma-and-theme-integration/devup-json',
46+
children: 'devup.json Configuration',
47+
},
48+
]}
49+
>
50+
Figma and Theme Integration
51+
</MenuItem>
2652
<MenuItem
2753
subMenu={[
2854
{
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
export const metadata = {
2+
title: 'N/M Base',
3+
alternates: {
4+
canonical: '/docs/core-concepts/nm-base',
5+
},
6+
}
7+
8+
# N/M Base
9+
10+
Devup UI uses a custom N/M base numbering system to generate compact, collision-free class names that are optimized for CSS constraints and ad-blocker compatibility.
11+
12+
- **Generates compact class names**: Short, efficient class names like `a`, `b`, `c`
13+
- **Avoids CSS constraints**: Class names never start with digits
14+
- **Prevents ad-blocker conflicts**: Avoids patterns that trigger content blockers
15+
- **Ensures uniqueness**: Each style gets a unique, deterministic class name
16+
- **Optimizes for size**: Minimal class name length for maximum efficiency
17+
18+
## How It Works
19+
20+
### **Base System**
21+
22+
The N/M base system uses two character sets:
23+
24+
- **N Base**: `a-z, _` (27 characters)
25+
- **M Base**: `a-z, 0-9, _` (37 characters)
26+
27+
### **Number Conversion**
28+
29+
Numbers are converted to N/M base using a two-phase approach:
30+
31+
1. **Zero handling**: Returns 'a' when the number is 0
32+
2. **Base conversion**: Uses base-26 to convert numbers to alphabetic characters
33+
- First digit: Uses N base array (a-z)
34+
- Remaining digits: Uses M base array (a-z)
35+
3. **Ad-blocker conflict prevention**: Changes result to "a-d" if it ends with "ad"
36+
37+
### **Class Name Generation**
38+
39+
Class name generation follows these steps:
40+
41+
1. **Style signature creation**: Combines property, level, value, selector, and style order to create a unique key
42+
2. **File identifier addition**: Converts filename to number for per-file CSS splitting
43+
3. **Sequential number assignment**: Assigns a sequential number based on the order of unique styles in the GLOBAL_CLASS_MAP
44+
4. **N/M base conversion**: Converts the sequential number to an alphabetic class name using the N/M base system
45+
5. **Final combination**: Combines file identifier and class number to create the final class name when file identifier exists
46+
47+
## Examples
48+
49+
### **Basic Class Names**
50+
51+
<div
52+
style={{
53+
display: 'grid',
54+
gridTemplateColumns: '1fr 1fr',
55+
gap: '2rem',
56+
marginBottom: '2rem',
57+
alignItems: 'start',
58+
}}
59+
>
60+
<div>
61+
```tsx // Input
62+
<div>
63+
<Box bg="red" />
64+
<Box bg="blue" />
65+
<Box color="white" />
66+
</div>
67+
```
68+
</div>
69+
<div>
70+
```tsx // Output (N/M base class names)
71+
<div>
72+
<Box className="a" /> {/* bg: red */}
73+
<Box className="b" /> {/* bg: blue */}
74+
<Box className="c" /> {/* color: white */}
75+
</div>
76+
```
77+
</div>
78+
</div>
79+
80+
### **Responsive Class Names**
81+
82+
<div
83+
style={{
84+
display: 'grid',
85+
gridTemplateColumns: '1fr 1fr',
86+
gap: '2rem',
87+
marginBottom: '2rem',
88+
alignItems: 'start',
89+
}}
90+
>
91+
<div>
92+
```tsx // Input
93+
<Box w={[100, 200, 300]} />
94+
```
95+
</div>
96+
<div>
97+
```tsx // Output
98+
<Box className="d e f" />
99+
{/* w: 100px, w: 200px, w: 300px */}
100+
```
101+
</div>
102+
</div>
103+
104+
### **Pseudo-selector Class Names**
105+
106+
<div
107+
style={{
108+
display: 'grid',
109+
gridTemplateColumns: '1fr 1fr',
110+
gap: '2rem',
111+
marginBottom: '2rem',
112+
alignItems: 'start',
113+
}}
114+
>
115+
<div>
116+
```tsx
117+
<Box _hover={{ bg: 'red' }} />
118+
```
119+
</div>
120+
<div>
121+
```tsx
122+
<Box className="g" />
123+
{/* .g:hover { background: red; } */}
124+
```
125+
</div>
126+
</div>
127+
128+
### **File-specific Class Names**
129+
130+
<div
131+
style={{
132+
display: 'grid',
133+
gridTemplateColumns: '1fr 1fr',
134+
gap: '2rem',
135+
marginBottom: '2rem',
136+
alignItems: 'start',
137+
}}
138+
>
139+
<div>
140+
```tsx
141+
<div>
142+
<Box bg="red" />
143+
<Box bg="red" />
144+
</div>
145+
```
146+
</div>
147+
<div>
148+
```tsx
149+
<div>
150+
<Box className="a" /> {/* file1.tsx */}
151+
<Box className="a-a" /> {/* file2.tsx */}
152+
</div>
153+
```
154+
</div>
155+
</div>
156+
157+
## Ad-blocker Compatibility
158+
159+
### **Why "ad" is Problematic**
160+
161+
Ad-blockers recognize and block class names containing "ad" patterns as advertisements:
162+
163+
- **Ad-blocker filters**: Classify elements containing "ad" strings as advertisements
164+
- **CSS blocking**: Styles are not applied, causing UI to break
165+
- **Poor user experience**: Unintended style blocking causes layout issues
166+
167+
### **Our Solution**
168+
169+
Devup UI prevents ad-blocker conflicts through the following methods:
170+
171+
- **Pattern avoidance**: Automatically converts class names ending with "ad" to "a-d"
172+
- **Safe character usage**: Uses only `a-z`, `-`, `_` to avoid blocking patterns
173+
- **No numbers**: Ensures class names don't start with digits to comply with CSS constraints
174+
175+
## Advantages
176+
177+
- **Compact class names**: First 26 styles generate single characters like `a`, `b`, `c`
178+
- **Build consistency**: Always generates the same class name for identical input
179+
- **Cache optimization**: Consistent class names improve browser cache efficiency
180+
- **Collision prevention**: Each style has a unique signature to prevent class name collisions
181+
- **Ad-blocker compatibility**: Automatically converts "ad" patterns to "a-d" to prevent blocking
182+
- **CSS constraint compliance**: Class names don't start with digits to comply with CSS rules

0 commit comments

Comments
 (0)