bsmnt 0.3.0 → 0.3.2
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/helpers/create/setup-sanity.d.ts.map +1 -1
- package/dist/helpers/create/setup-sanity.js +19 -1
- package/dist/helpers/create/setup-sanity.js.map +1 -1
- package/dist/helpers/integrate/sanity/config.d.ts.map +1 -1
- package/dist/helpers/integrate/sanity/config.js +11 -2
- package/dist/helpers/integrate/sanity/config.js.map +1 -1
- package/index.js +2 -2
- package/package.json +1 -1
- package/src/templates/next-default/.vscode/settings.json +1 -1
- package/src/templates/next-default/README.md +6 -7
- package/src/templates/next-default/app/layout.tsx +17 -4
- package/src/templates/next-default/biome.json +1 -1
- package/src/templates/next-default/css.d.ts +1 -0
- package/src/templates/next-default/lib/README.md +4 -8
- package/src/templates/next-default/lib/hooks/use-media.ts +3 -1
- package/src/templates/next-default/lib/styles/global.css +182 -0
- package/src/templates/next-default/lib/utils/json-ld.tsx +13 -18
- package/src/templates/next-default/lib/utils/portable-text-to-markdown.ts +83 -0
- package/src/templates/next-default/package.json +3 -3
- package/src/templates/next-experiments/.vscode/settings.json +1 -1
- package/src/templates/next-experiments/README.md +6 -7
- package/src/templates/next-experiments/app/layout.tsx +17 -4
- package/src/templates/next-experiments/biome.json +1 -1
- package/src/templates/next-experiments/css.d.ts +1 -0
- package/src/templates/next-experiments/lib/README.md +4 -8
- package/src/templates/next-experiments/lib/hooks/use-media.ts +3 -1
- package/src/templates/next-experiments/lib/styles/global.css +182 -0
- package/src/templates/next-experiments/lib/utils/json-ld.tsx +13 -18
- package/src/templates/next-experiments/lib/utils/portable-text-to-markdown.ts +83 -0
- package/src/templates/next-experiments/package.json +3 -3
- package/src/templates/next-webgl/.vscode/settings.json +1 -1
- package/src/templates/next-webgl/README.md +6 -7
- package/src/templates/next-webgl/app/layout.tsx +17 -4
- package/src/templates/next-webgl/biome.json +1 -1
- package/src/templates/next-webgl/css.d.ts +1 -0
- package/src/templates/next-webgl/lib/README.md +4 -8
- package/src/templates/next-webgl/lib/hooks/use-media.ts +3 -1
- package/src/templates/next-webgl/lib/styles/global.css +182 -0
- package/src/templates/next-webgl/lib/utils/json-ld.tsx +13 -18
- package/src/templates/next-webgl/lib/utils/portable-text-to-markdown.ts +83 -0
- package/src/templates/next-webgl/package.json +3 -3
- package/src/templates/next-default/lib/scripts/dev.ts +0 -32
- package/src/templates/next-default/lib/styles/README.md +0 -13
- package/src/templates/next-default/lib/styles/fonts.ts +0 -20
- package/src/templates/next-default/lib/styles/index.css +0 -3
- package/src/templates/next-default/lib/styles/tokens.css +0 -179
- package/src/templates/next-default/lib/utils/README.md +0 -40
- package/src/templates/next-default/lib/utils/easings.ts +0 -240
- package/src/templates/next-default/lib/utils/fetch.ts +0 -84
- package/src/templates/next-default/lib/utils/global-css.d.ts +0 -1
- package/src/templates/next-default/lib/utils/math.ts +0 -236
- package/src/templates/next-default/lib/utils/strings.ts +0 -246
- package/src/templates/next-default/lib/utils/types.d.ts +0 -15
- package/src/templates/next-default/lib/utils/viewport.ts +0 -199
- package/src/templates/next-experiments/lib/scripts/dev.ts +0 -32
- package/src/templates/next-experiments/lib/styles/README.md +0 -13
- package/src/templates/next-experiments/lib/styles/fonts.ts +0 -20
- package/src/templates/next-experiments/lib/styles/index.css +0 -3
- package/src/templates/next-experiments/lib/styles/tokens.css +0 -179
- package/src/templates/next-experiments/lib/utils/README.md +0 -40
- package/src/templates/next-experiments/lib/utils/easings.ts +0 -240
- package/src/templates/next-experiments/lib/utils/fetch.ts +0 -84
- package/src/templates/next-experiments/lib/utils/global-css.d.ts +0 -1
- package/src/templates/next-experiments/lib/utils/math.ts +0 -236
- package/src/templates/next-experiments/lib/utils/strings.ts +0 -246
- package/src/templates/next-experiments/lib/utils/types.d.ts +0 -15
- package/src/templates/next-experiments/lib/utils/viewport.ts +0 -199
- package/src/templates/next-webgl/lib/scripts/dev.ts +0 -32
- package/src/templates/next-webgl/lib/styles/README.md +0 -13
- package/src/templates/next-webgl/lib/styles/fonts.ts +0 -20
- package/src/templates/next-webgl/lib/styles/index.css +0 -3
- package/src/templates/next-webgl/lib/styles/tokens.css +0 -179
- package/src/templates/next-webgl/lib/utils/README.md +0 -40
- package/src/templates/next-webgl/lib/utils/easings.ts +0 -240
- package/src/templates/next-webgl/lib/utils/fetch.ts +0 -84
- package/src/templates/next-webgl/lib/utils/global-css.d.ts +0 -1
- package/src/templates/next-webgl/lib/utils/math.ts +0 -236
- package/src/templates/next-webgl/lib/utils/strings.ts +0 -246
- package/src/templates/next-webgl/lib/utils/types.d.ts +0 -15
- package/src/templates/next-webgl/lib/utils/viewport.ts +0 -199
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* String & Object Utilities
|
|
3
|
-
*
|
|
4
|
-
* Helper functions for string manipulation, object operations, and refs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Ref } from "react"
|
|
8
|
-
|
|
9
|
-
// =============================================================================
|
|
10
|
-
// STRING UTILITIES
|
|
11
|
-
// =============================================================================
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Converts text to URL-friendly slug format.
|
|
15
|
-
*
|
|
16
|
-
* @param text - Text to convert (must have toString method)
|
|
17
|
-
* @returns URL-safe slug string
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* slugify('Hello World!') // 'hello-world'
|
|
22
|
-
* slugify('Café & Restaurant') // 'cafe-restaurant'
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export function slugify(text: { toString: () => string }) {
|
|
26
|
-
return text
|
|
27
|
-
.toString()
|
|
28
|
-
.normalize("NFKD")
|
|
29
|
-
.toLowerCase()
|
|
30
|
-
.trim()
|
|
31
|
-
.replace(/\s+/g, "-")
|
|
32
|
-
.replace(/[^\w-]+/g, "")
|
|
33
|
-
.replace(/--+/g, "-")
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Converts first character to lowercase (camelCase format).
|
|
38
|
-
*
|
|
39
|
-
* @param inputString - String to convert
|
|
40
|
-
* @returns String with lowercase first character
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* convertToCamelCase('HelloWorld') // 'helloWorld'
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export function convertToCamelCase(inputString: string) {
|
|
48
|
-
return inputString.charAt(0).toLowerCase() + inputString.slice(1)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Capitalizes the first letter of a string.
|
|
53
|
-
*
|
|
54
|
-
* @param inputString - String to capitalize
|
|
55
|
-
* @returns String with capitalized first letter
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```ts
|
|
59
|
-
* capitalizeFirstLetter('hello') // 'Hello'
|
|
60
|
-
* capitalizeFirstLetter('world') // 'World'
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
export function capitalizeFirstLetter(inputString: string) {
|
|
64
|
-
return inputString.charAt(0).toUpperCase() + inputString.slice(1)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Formats a number as a two-digit string (padding with zero).
|
|
69
|
-
*
|
|
70
|
-
* @param number - Number to format
|
|
71
|
-
* @returns Two-digit string representation
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```ts
|
|
75
|
-
* twoDigits(5) // '05'
|
|
76
|
-
* twoDigits(23) // '23'
|
|
77
|
-
* ```
|
|
78
|
-
*/
|
|
79
|
-
export function twoDigits(number: number) {
|
|
80
|
-
return number > 9 ? `${number}` : `0${number}`
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Adds commas as thousands separators to numbers.
|
|
85
|
-
*
|
|
86
|
-
* @param x - Number to format (must have toString method)
|
|
87
|
-
* @returns String with comma separators
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```ts
|
|
91
|
-
* numberWithCommas(1234) // '1,234'
|
|
92
|
-
* numberWithCommas(1234567) // '1,234,567'
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
export function numberWithCommas(x: { toString: () => string }) {
|
|
96
|
-
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// =============================================================================
|
|
100
|
-
// ARRAY & OBJECT UTILITIES
|
|
101
|
-
// =============================================================================
|
|
102
|
-
|
|
103
|
-
export function checkIsArray<T>(value: T): T extends unknown[] ? T[0] : T {
|
|
104
|
-
return (Array.isArray(value) ? value[0] : value) as T extends unknown[]
|
|
105
|
-
? T[0]
|
|
106
|
-
: T
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Checks if an object is empty (has no enumerable properties).
|
|
111
|
-
*
|
|
112
|
-
* @param obj - Object to check
|
|
113
|
-
* @returns True if object is empty or null/undefined
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* ```ts
|
|
117
|
-
* isEmptyObject({}) // true
|
|
118
|
-
* isEmptyObject({ a: 1 }) // false
|
|
119
|
-
* isEmptyObject(null) // true
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
export function isEmptyObject(obj: Record<string, unknown>) {
|
|
123
|
-
if (!obj) return true
|
|
124
|
-
return Object.keys(obj).length === 0
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Checks if an array is empty or if the input is not an array.
|
|
129
|
-
*
|
|
130
|
-
* @param arr - Array or value to check
|
|
131
|
-
* @returns True if empty array or not an array
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```ts
|
|
135
|
-
* isEmptyArray([]) // true
|
|
136
|
-
* isEmptyArray([1, 2]) // false
|
|
137
|
-
* isEmptyArray('test') // true (not an array)
|
|
138
|
-
* isEmptyArray(null) // true
|
|
139
|
-
* ```
|
|
140
|
-
*/
|
|
141
|
-
export function isEmptyArray(arr: string | unknown[]) {
|
|
142
|
-
if (!arr) return true
|
|
143
|
-
return Array.isArray(arr) && arr.length === 0
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function arraytoObject(array: Record<string, unknown>[]) {
|
|
147
|
-
return array.reduce((acc: Record<string, unknown>, currentObj) => {
|
|
148
|
-
const key = Object.keys(currentObj)[0]
|
|
149
|
-
if (key !== undefined) {
|
|
150
|
-
acc[key] = currentObj[key]
|
|
151
|
-
}
|
|
152
|
-
return acc
|
|
153
|
-
}, {})
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function shortenObjectKeys(
|
|
157
|
-
obj: Record<string, unknown>,
|
|
158
|
-
keyword: string
|
|
159
|
-
) {
|
|
160
|
-
const regex = new RegExp(`[^]+${keyword}(.*)`)
|
|
161
|
-
|
|
162
|
-
for (const key in obj) {
|
|
163
|
-
const match = key.match(regex)
|
|
164
|
-
|
|
165
|
-
if (match?.[1]) {
|
|
166
|
-
const newKey = convertToCamelCase(match[1])
|
|
167
|
-
obj[newKey] = obj[key]
|
|
168
|
-
delete obj[key]
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return obj
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function filterObjectKeys(
|
|
176
|
-
obj: { [x: string]: unknown },
|
|
177
|
-
keyword: string
|
|
178
|
-
) {
|
|
179
|
-
const newObj: { [x: string]: unknown } = {}
|
|
180
|
-
|
|
181
|
-
for (const key in obj) {
|
|
182
|
-
const match = key.includes(keyword)
|
|
183
|
-
|
|
184
|
-
if (match) {
|
|
185
|
-
newObj[key] = obj[key]
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return newObj
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function iterableObject(
|
|
193
|
-
obj: { [s: string]: unknown } | ArrayLike<unknown>
|
|
194
|
-
) {
|
|
195
|
-
return Object.entries(obj).map(([, value]) => {
|
|
196
|
-
return value
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// =============================================================================
|
|
201
|
-
// REF UTILITIES
|
|
202
|
-
// =============================================================================
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Merges multiple React refs into a single ref callback.
|
|
206
|
-
*
|
|
207
|
-
* Useful when you need to assign multiple refs to the same element,
|
|
208
|
-
* such as combining a local ref with a forwarded ref.
|
|
209
|
-
*
|
|
210
|
-
* @param refs - Refs to merge (functions and ref objects supported)
|
|
211
|
-
* @returns A ref callback that updates all provided refs
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* ```tsx
|
|
215
|
-
* // Component that needs both local and forwarded ref
|
|
216
|
-
* function MyComponent({ ref }: { ref: React.Ref<HTMLDivElement> }) {
|
|
217
|
-
* const localRef = useRef<HTMLDivElement>(null)
|
|
218
|
-
* const mergedRef = mergeRefs(localRef, ref)
|
|
219
|
-
*
|
|
220
|
-
* return <div ref={mergedRef}>Content</div>
|
|
221
|
-
* }
|
|
222
|
-
*
|
|
223
|
-
* // Forward ref pattern
|
|
224
|
-
* const ForwardedComponent = forwardRef(MyComponent)
|
|
225
|
-
* ```
|
|
226
|
-
*/
|
|
227
|
-
export function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): Ref<T> {
|
|
228
|
-
return (value) => {
|
|
229
|
-
const cleanups = refs.reduce<VoidFunction[]>((accumulator, ref) => {
|
|
230
|
-
if (typeof ref === "function") {
|
|
231
|
-
const cleanup = ref(value)
|
|
232
|
-
if (typeof cleanup === "function") {
|
|
233
|
-
accumulator.push(cleanup)
|
|
234
|
-
}
|
|
235
|
-
} else if (ref) {
|
|
236
|
-
ref.current = value
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return accumulator
|
|
240
|
-
}, [])
|
|
241
|
-
|
|
242
|
-
return () => {
|
|
243
|
-
for (const cleanup of cleanups) cleanup()
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Type augmentations and module declarations
|
|
2
|
-
|
|
3
|
-
// React CSS custom properties support
|
|
4
|
-
import "react"
|
|
5
|
-
|
|
6
|
-
declare module "react" {
|
|
7
|
-
interface CSSProperties {
|
|
8
|
-
[key: `--${string}`]: string | number
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Global window extensions
|
|
13
|
-
declare global {
|
|
14
|
-
interface Window {}
|
|
15
|
-
}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Viewport Utilities
|
|
3
|
-
*
|
|
4
|
-
* Convert pixel values to viewport-relative sizes and relative units.
|
|
5
|
-
* Mirrors the SASS functions for consistent responsive scaling.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { tovw, torem, toem } from '@/utils/viewport'
|
|
10
|
-
*
|
|
11
|
-
* // Convert to viewport width units
|
|
12
|
-
* const paddingX = tovw(16, 24) // "1.5625vw" with a minimum of 24px
|
|
13
|
-
* const fontSize = tovw(16, 'mobile') // "4.27vw" (no undefined needed!)
|
|
14
|
-
* const width = tovw(100, 50, 'desktop') // "max(50px, 6.94vw)"
|
|
15
|
-
*
|
|
16
|
-
* // Convert to rem/em
|
|
17
|
-
* const spacing = torem(24) // "1.5rem"
|
|
18
|
-
* const padding = toem(16, 14) // "1.14em"
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* ## When to Use
|
|
22
|
-
*
|
|
23
|
-
* - **CSS**: Prefer the PostCSS functions `tovw()`, `torem()`, and `toem()`
|
|
24
|
-
* - **JavaScript/TypeScript**: Use these for dynamic calculations or inline styles
|
|
25
|
-
* - **Runtime calculations**: Use for canvas, WebGL, or dynamic sizing
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
const BREAKPOINTS = {
|
|
29
|
-
"desktop-large": 1920,
|
|
30
|
-
desktop: 1440,
|
|
31
|
-
"tablet-lg": 1024,
|
|
32
|
-
tablet: 768,
|
|
33
|
-
mobile: 640,
|
|
34
|
-
} as const
|
|
35
|
-
|
|
36
|
-
type ContextSize = keyof typeof BREAKPOINTS | number
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Resolves a context size to a pixel value.
|
|
40
|
-
*
|
|
41
|
-
* @param context - Context identifier or pixel value
|
|
42
|
-
* @returns Pixel value
|
|
43
|
-
*/
|
|
44
|
-
function resolveContext(context: ContextSize): number {
|
|
45
|
-
if (typeof context === "number") {
|
|
46
|
-
return context
|
|
47
|
-
}
|
|
48
|
-
return BREAKPOINTS[context] ?? BREAKPOINTS.desktop
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Checks if a value is a context identifier (breakpoint key).
|
|
53
|
-
*
|
|
54
|
-
* @param value - Value to check
|
|
55
|
-
* @returns True if value is a breakpoint key
|
|
56
|
-
*/
|
|
57
|
-
function isContextIdentifier(
|
|
58
|
-
value: unknown
|
|
59
|
-
): value is keyof typeof BREAKPOINTS {
|
|
60
|
-
return typeof value === "string" && value in BREAKPOINTS
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Rounds a number to a maximum of 4 decimal places, removing trailing zeros.
|
|
65
|
-
*
|
|
66
|
-
* @param value - Number to round
|
|
67
|
-
* @returns Rounded number as string without trailing zeros
|
|
68
|
-
*/
|
|
69
|
-
function roundToMaxDecimals(value: number): string {
|
|
70
|
-
// Round to 4 decimal places
|
|
71
|
-
const rounded = Math.round(value * 10000) / 10000
|
|
72
|
-
// Convert to string and remove trailing zeros only after decimal point
|
|
73
|
-
return rounded
|
|
74
|
-
.toString()
|
|
75
|
-
.replace(/\.0+$/, "")
|
|
76
|
-
.replace(/(\.\d*?)0+$/, "$1")
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Function overloads for tovw
|
|
80
|
-
export function tovw(target: number): string
|
|
81
|
-
export function tovw(target: number, min: number): string
|
|
82
|
-
export function tovw(target: number, context: ContextSize): string
|
|
83
|
-
export function tovw(target: number, min: number, context: ContextSize): string
|
|
84
|
-
/**
|
|
85
|
-
* Converts a pixel value to viewport width units (vw).
|
|
86
|
-
* Optionally applies a minimum value using CSS max().
|
|
87
|
-
*
|
|
88
|
-
* Supports flexible parameter patterns:
|
|
89
|
-
* - `tovw(target)` - Uses default desktop context
|
|
90
|
-
* - `tovw(target, min)` - Sets minimum value
|
|
91
|
-
* - `tovw(target, context)` - Sets context (auto-detected if string identifier)
|
|
92
|
-
* - `tovw(target, min, context)` - Sets both min and context
|
|
93
|
-
*
|
|
94
|
-
* @param target - Target pixel value to convert
|
|
95
|
-
* @param minOrContext - Optional minimum pixel value OR context identifier
|
|
96
|
-
* @param context - Context size identifier (if min was provided)
|
|
97
|
-
* @returns CSS string with vw units (e.g., "6.94vw" or "max(50px, 6.94vw)")
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* ```ts
|
|
101
|
-
* // Basic conversion
|
|
102
|
-
* tovw(100) // "6.94vw"
|
|
103
|
-
* tovw(16, 'mobile') // "4.27vw" (no undefined needed!)
|
|
104
|
-
*
|
|
105
|
-
* // With minimum value
|
|
106
|
-
* tovw(100, 50) // "max(50px, 6.94vw)"
|
|
107
|
-
* tovw(100, 50, 'mobile') // "max(50px, 6.94vw)" with mobile context
|
|
108
|
-
*
|
|
109
|
-
* // Custom context
|
|
110
|
-
* tovw(100, 1920) // "5.21vw"
|
|
111
|
-
*
|
|
112
|
-
* // Handles zero
|
|
113
|
-
* tovw(0) // "0"
|
|
114
|
-
* ```
|
|
115
|
-
*/
|
|
116
|
-
export function tovw(
|
|
117
|
-
target: number,
|
|
118
|
-
minOrContext?: number | ContextSize,
|
|
119
|
-
context?: ContextSize
|
|
120
|
-
): string {
|
|
121
|
-
if (target === 0) {
|
|
122
|
-
return "0"
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
let min: number | undefined
|
|
126
|
-
let resolvedContext: ContextSize = "desktop"
|
|
127
|
-
|
|
128
|
-
if (minOrContext !== undefined) {
|
|
129
|
-
if (isContextIdentifier(minOrContext)) {
|
|
130
|
-
// Second param is context
|
|
131
|
-
resolvedContext = minOrContext
|
|
132
|
-
if (context !== undefined) {
|
|
133
|
-
resolvedContext = context
|
|
134
|
-
}
|
|
135
|
-
} else if (typeof minOrContext === "number") {
|
|
136
|
-
// Second param is min value
|
|
137
|
-
min = minOrContext
|
|
138
|
-
if (context !== undefined) {
|
|
139
|
-
resolvedContext = context
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} else if (context !== undefined) {
|
|
143
|
-
// Only context provided as third param (legacy support)
|
|
144
|
-
resolvedContext = context
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const contextSize = resolveContext(resolvedContext)
|
|
148
|
-
const vwValue = (target / contextSize) * 100
|
|
149
|
-
|
|
150
|
-
if (min !== undefined) {
|
|
151
|
-
return `max(${min}px, ${roundToMaxDecimals(vwValue)}vw)`
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return `${roundToMaxDecimals(vwValue)}vw`
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Converts a pixel value to rem units.
|
|
159
|
-
*
|
|
160
|
-
* @param target - Target pixel value to convert
|
|
161
|
-
* @param context - Base font size in pixels (default: 16)
|
|
162
|
-
* @returns CSS string with rem units (e.g., "1.5rem")
|
|
163
|
-
*
|
|
164
|
-
* @example
|
|
165
|
-
* ```ts
|
|
166
|
-
* torem(24) // "1.5rem" (24 / 16)
|
|
167
|
-
* torem(18, 14) // "1.29rem" (18 / 14)
|
|
168
|
-
* torem(0) // "0"
|
|
169
|
-
* ```
|
|
170
|
-
*/
|
|
171
|
-
export function torem(target: number, context = 16): string {
|
|
172
|
-
if (target === 0) {
|
|
173
|
-
return "0"
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return `${roundToMaxDecimals(target / context)}rem`
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Converts a pixel value to em units.
|
|
181
|
-
*
|
|
182
|
-
* @param target - Target pixel value to convert
|
|
183
|
-
* @param context - Context size in pixels (required)
|
|
184
|
-
* @returns CSS string with em units (e.g., "1.5em")
|
|
185
|
-
*
|
|
186
|
-
* @example
|
|
187
|
-
* ```ts
|
|
188
|
-
* toem(24, 16) // "1.5em" (24 / 16)
|
|
189
|
-
* toem(18, 14) // "1.29em" (18 / 14)
|
|
190
|
-
* toem(0, 16) // "0"
|
|
191
|
-
* ```
|
|
192
|
-
*/
|
|
193
|
-
export function toem(target: number, context: number): string {
|
|
194
|
-
if (target === 0) {
|
|
195
|
-
return "0"
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return `${roundToMaxDecimals(target / context)}em`
|
|
199
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-platform Next.js dev launcher.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { bunExecutable, colorEnv } from "./utils"
|
|
6
|
-
|
|
7
|
-
const isHttps = process.argv.includes("--https")
|
|
8
|
-
const isInspect = process.argv.includes("--inspect")
|
|
9
|
-
|
|
10
|
-
// Build next dev command args
|
|
11
|
-
const nextDevArgs = [bunExecutable, "next", "dev"]
|
|
12
|
-
if (isHttps) nextDevArgs.push("--experimental-https")
|
|
13
|
-
if (isInspect) nextDevArgs.push("--inspect")
|
|
14
|
-
|
|
15
|
-
const devEnv = colorEnv()
|
|
16
|
-
|
|
17
|
-
const nextDevProcess = Bun.spawn(nextDevArgs, {
|
|
18
|
-
stdout: "inherit",
|
|
19
|
-
stderr: "inherit",
|
|
20
|
-
env: devEnv,
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const cleanup = () => {
|
|
24
|
-
nextDevProcess.kill()
|
|
25
|
-
process.exit(0)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
process.on("SIGINT", cleanup)
|
|
29
|
-
process.on("SIGTERM", cleanup)
|
|
30
|
-
|
|
31
|
-
await nextDevProcess.exited
|
|
32
|
-
cleanup()
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Styles
|
|
2
|
-
|
|
3
|
-
Minimal Tailwind setup for the starter.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Purpose |
|
|
8
|
-
|------|---------|
|
|
9
|
-
| `index.css` | Single stylesheet entrypoint imported by `app/layout.tsx` |
|
|
10
|
-
| `tokens.css` | Theme variables, breakpoints, and custom Tailwind utilities |
|
|
11
|
-
| `global.css` | Reset and global app styles |
|
|
12
|
-
| `fonts.ts` | Font variable setup |
|
|
13
|
-
| `cn.ts` | Tailwind class merging helper |
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Geist_Mono } from "next/font/google";
|
|
2
|
-
|
|
3
|
-
const mono = Geist_Mono({
|
|
4
|
-
subsets: ["latin"],
|
|
5
|
-
display: "swap",
|
|
6
|
-
variable: "--geist-mono",
|
|
7
|
-
fallback: [
|
|
8
|
-
"ui-monospace",
|
|
9
|
-
"SFMono-Regular",
|
|
10
|
-
"Consolas",
|
|
11
|
-
"Liberation Mono",
|
|
12
|
-
"Menlo",
|
|
13
|
-
"monospace",
|
|
14
|
-
],
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const fonts = [mono];
|
|
18
|
-
const fontsVariable = fonts.map((font) => font.variable).join(" ");
|
|
19
|
-
|
|
20
|
-
export { fontsVariable };
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
@custom-media --hover (hover: hover);
|
|
2
|
-
@custom-media --reduced-motion (prefers-reduced-motion: reduce);
|
|
3
|
-
@custom-media --mobile-only (width < 768px);
|
|
4
|
-
@custom-media --tablet (width >= 768px);
|
|
5
|
-
@custom-media --tablet-lg (width >= 1024px);
|
|
6
|
-
@custom-media --desktop (width >= 1440px);
|
|
7
|
-
@custom-media --desktop-large (width >= 1920px);
|
|
8
|
-
|
|
9
|
-
:root {
|
|
10
|
-
--device-width: 640;
|
|
11
|
-
--device-height: 650;
|
|
12
|
-
--columns: 4;
|
|
13
|
-
--gap: 16px;
|
|
14
|
-
--safe: 16px;
|
|
15
|
-
--header-height: 58px;
|
|
16
|
-
--layout-width: calc(100vw - (2 * var(--safe)));
|
|
17
|
-
--column-width: calc(
|
|
18
|
-
(var(--layout-width) - (var(--columns) - 1) * var(--gap)) / var(--columns)
|
|
19
|
-
);
|
|
20
|
-
--ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
|
21
|
-
--ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
|
22
|
-
--ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
|
|
23
|
-
--ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
|
24
|
-
--ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
|
|
25
|
-
--ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
|
|
26
|
-
--ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
27
|
-
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
28
|
-
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
|
|
29
|
-
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
|
|
30
|
-
--ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
|
|
31
|
-
--ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
|
|
32
|
-
--ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
|
|
33
|
-
--ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
34
|
-
--ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
|
|
35
|
-
--ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
|
|
36
|
-
--ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
|
|
37
|
-
--ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
|
|
38
|
-
--ease-gleasing: cubic-bezier(0.4, 0, 0, 1);
|
|
39
|
-
--color-black: #000000;
|
|
40
|
-
--color-white: #ffffff;
|
|
41
|
-
--color-orange: #ff4d00;
|
|
42
|
-
--color-blue: #487cff;
|
|
43
|
-
--color-green: #00ff9b;
|
|
44
|
-
--color-violet: #f101a5;
|
|
45
|
-
--color-pink: #ff73a6;
|
|
46
|
-
--color-gray: #666666;
|
|
47
|
-
|
|
48
|
-
@variant desktop {
|
|
49
|
-
--device-width: 1440;
|
|
50
|
-
--device-height: 816;
|
|
51
|
-
--columns: 12;
|
|
52
|
-
--header-height: 98px;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@theme {
|
|
57
|
-
--breakpoint-*: initial;
|
|
58
|
-
--breakpoint-desktop-large: 1920px;
|
|
59
|
-
--breakpoint-desktop: 1440px;
|
|
60
|
-
--breakpoint-tablet-lg: 1024px;
|
|
61
|
-
--breakpoint-tablet: 768px;
|
|
62
|
-
--breakpoint-mobile: 640px;
|
|
63
|
-
|
|
64
|
-
--color-*: initial;
|
|
65
|
-
--color-primary: #ffffff;
|
|
66
|
-
--color-secondary: #000000;
|
|
67
|
-
--color-contrast: #ff4d00;
|
|
68
|
-
--color-black: #000000;
|
|
69
|
-
--color-white: #ffffff;
|
|
70
|
-
--color-orange: #ff4d00;
|
|
71
|
-
--color-blue: #487cff;
|
|
72
|
-
--color-green: #00ff9b;
|
|
73
|
-
--color-violet: #f101a5;
|
|
74
|
-
--color-pink: #ff73a6;
|
|
75
|
-
--color-gray: #666666;
|
|
76
|
-
--color-gray-50: #f5f5f5;
|
|
77
|
-
--color-gray-100: #e0e0e0;
|
|
78
|
-
--color-gray-200: #c2c2c2;
|
|
79
|
-
--color-gray-300: #a3a3a3;
|
|
80
|
-
--color-gray-400: #858585;
|
|
81
|
-
--color-gray-500: #666666;
|
|
82
|
-
--color-gray-600: #4d4d4d;
|
|
83
|
-
--color-gray-700: #333333;
|
|
84
|
-
--color-gray-800: #1a1a1a;
|
|
85
|
-
|
|
86
|
-
--spacing: 0.25rem;
|
|
87
|
-
--spacing-0: 0;
|
|
88
|
-
--spacing-safe: var(--safe);
|
|
89
|
-
--spacing-gap: var(--gap);
|
|
90
|
-
--spacing-header-height: var(--header-height);
|
|
91
|
-
|
|
92
|
-
--font-*: initial;
|
|
93
|
-
--font-mono: var(--geist-mono);
|
|
94
|
-
|
|
95
|
-
--ease-*: initial;
|
|
96
|
-
--ease-in-quad: var(--ease-in-quad);
|
|
97
|
-
--ease-in-cubic: var(--ease-in-cubic);
|
|
98
|
-
--ease-in-quart: var(--ease-in-quart);
|
|
99
|
-
--ease-in-quint: var(--ease-in-quint);
|
|
100
|
-
--ease-in-expo: var(--ease-in-expo);
|
|
101
|
-
--ease-in-circ: var(--ease-in-circ);
|
|
102
|
-
--ease-out-quad: var(--ease-out-quad);
|
|
103
|
-
--ease-out-cubic: var(--ease-out-cubic);
|
|
104
|
-
--ease-out-quart: var(--ease-out-quart);
|
|
105
|
-
--ease-out-quint: var(--ease-out-quint);
|
|
106
|
-
--ease-out-expo: var(--ease-out-expo);
|
|
107
|
-
--ease-out-circ: var(--ease-out-circ);
|
|
108
|
-
--ease-in-out-quad: var(--ease-in-out-quad);
|
|
109
|
-
--ease-in-out-cubic: var(--ease-in-out-cubic);
|
|
110
|
-
--ease-in-out-quart: var(--ease-in-out-quart);
|
|
111
|
-
--ease-in-out-quint: var(--ease-in-out-quint);
|
|
112
|
-
--ease-in-out-expo: var(--ease-in-out-expo);
|
|
113
|
-
--ease-in-out-circ: var(--ease-in-out-circ);
|
|
114
|
-
--ease-gleasing: var(--ease-gleasing);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
[data-theme="light"] {
|
|
118
|
-
--color-primary: #ffffff;
|
|
119
|
-
--color-secondary: #000000;
|
|
120
|
-
--color-contrast: #ff4d00;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
[data-theme="dark"] {
|
|
124
|
-
--color-primary: #000000;
|
|
125
|
-
--color-secondary: #ffffff;
|
|
126
|
-
--color-contrast: #ff4d00;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@utility test-mono {
|
|
130
|
-
font-family: var(--geist-mono);
|
|
131
|
-
font-size: 20px;
|
|
132
|
-
font-style: normal;
|
|
133
|
-
font-weight: 400;
|
|
134
|
-
letter-spacing: 0;
|
|
135
|
-
line-height: 90%;
|
|
136
|
-
|
|
137
|
-
@variant desktop {
|
|
138
|
-
font-size: 24px;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
@utility desktop-only {
|
|
143
|
-
@media (--mobile-only) {
|
|
144
|
-
display: none !important;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
@utility mobile-only {
|
|
149
|
-
@media (--tablet) {
|
|
150
|
-
display: none !important;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
@utility b-grid {
|
|
155
|
-
column-gap: var(--gap);
|
|
156
|
-
display: grid;
|
|
157
|
-
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
@utility b-layout-block {
|
|
161
|
-
margin-inline: auto;
|
|
162
|
-
width: calc(100% - 2 * var(--safe));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
@utility b-layout-block-inner {
|
|
166
|
-
padding-inline: var(--safe);
|
|
167
|
-
width: 100%;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
@utility b-layout-grid {
|
|
171
|
-
@apply b-layout-block b-grid;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
@utility b-layout-grid-inner {
|
|
175
|
-
@apply b-layout-block-inner b-grid;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
@custom-variant light (&:where([data-theme="light"], [data-theme="light"] *));
|
|
179
|
-
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|