qstd 0.1.2 → 0.1.4
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/README.md +768 -0
- package/dist/preset/index.cjs +21 -1
- package/dist/preset/index.js +21 -1
- package/dist/react/index.css +7 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/package.json +18 -17
package/README.md
CHANGED
|
@@ -88,11 +88,19 @@ import { defineConfig } from "@pandacss/dev";
|
|
|
88
88
|
import qstdPreset from "qstd/preset";
|
|
89
89
|
|
|
90
90
|
export default defineConfig({
|
|
91
|
+
preflight: true,
|
|
92
|
+
|
|
91
93
|
// IMPORTANT: Include base preset to get default colors (neutral, red, blue, etc.)
|
|
92
94
|
presets: ["@pandacss/dev/presets", qstdPreset],
|
|
93
95
|
|
|
94
96
|
include: ["./src/**/*.{ts,tsx}"],
|
|
95
97
|
|
|
98
|
+
outdir: "styled-system",
|
|
99
|
+
|
|
100
|
+
// REQUIRED: Enables Panda CSS to detect props on the Block component
|
|
101
|
+
// Without this, styles like bg="red" won't generate CSS utilities
|
|
102
|
+
jsxFramework: "react",
|
|
103
|
+
|
|
96
104
|
theme: {
|
|
97
105
|
extend: {
|
|
98
106
|
// Your custom theme
|
|
@@ -101,6 +109,8 @@ export default defineConfig({
|
|
|
101
109
|
});
|
|
102
110
|
```
|
|
103
111
|
|
|
112
|
+
**⚠️ Critical:** The `jsxFramework: "react"` setting is **required** for the Block component to work correctly. Without it, Panda CSS cannot detect style props like `bg="red"` on the Block component, and no CSS utilities will be generated.
|
|
113
|
+
|
|
104
114
|
## Global Types
|
|
105
115
|
|
|
106
116
|
When you install qstd, these types become **globally available** (no import needed):
|
|
@@ -121,6 +131,764 @@ When you install qstd, these types become **globally available** (no import need
|
|
|
121
131
|
## Documentation
|
|
122
132
|
|
|
123
133
|
See [SUMMARY.md](./SUMMARY.md) for complete package contents and [QSTD_PACKAGE_PRD.md](./QSTD_PACKAGE_PRD.md) for full specification.
|
|
134
|
+
For the interactive development playground showcasing Block variants, see [playground/README.md](./playground/README.md).
|
|
135
|
+
|
|
136
|
+
## Block Component Documentation
|
|
137
|
+
|
|
138
|
+
The `Block` component is a universal building block that replaces most HTML elements with an intelligent, prop-driven API powered by PandaCSS. It supports semantic HTML, grid/flex layouts, compound components, and extensive styling utilities.
|
|
139
|
+
|
|
140
|
+
### Quick Start
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import Block from "qstd/react";
|
|
144
|
+
|
|
145
|
+
// Simplest text
|
|
146
|
+
<Block>Hello World</Block>
|
|
147
|
+
|
|
148
|
+
// Text with styling
|
|
149
|
+
<Block is="txt" fontSize="lg" color="blue.500">
|
|
150
|
+
Styled text
|
|
151
|
+
</Block>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Core Concepts
|
|
155
|
+
|
|
156
|
+
#### The `is` Prop (Component Type)
|
|
157
|
+
|
|
158
|
+
The `is` prop tells Block what kind of component to render:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// Text elements
|
|
162
|
+
<Block is="txt">Default paragraph</Block>
|
|
163
|
+
<Block is="txt" as="h1">Heading 1</Block>
|
|
164
|
+
<Block is="txt" as="span">Inline text</Block>
|
|
165
|
+
|
|
166
|
+
// Interactive elements
|
|
167
|
+
<Block is="btn" onClick={handleClick}>Button</Block>
|
|
168
|
+
<Block is="link" onClick={navigate}>Link-styled button</Block>
|
|
169
|
+
<Block is="checkbox" checked={isChecked} onChecked={setIsChecked}>
|
|
170
|
+
Accept terms
|
|
171
|
+
</Block>
|
|
172
|
+
|
|
173
|
+
// Inputs
|
|
174
|
+
<Block is="input" placeholder="Enter text" />
|
|
175
|
+
<Block is="textarea" placeholder="Enter description" />
|
|
176
|
+
|
|
177
|
+
// Complex components
|
|
178
|
+
<Block is="progress" value={50} max={100} />
|
|
179
|
+
<Block is="drawer" open={isOpen} onClose={handleClose}>
|
|
180
|
+
Drawer content
|
|
181
|
+
</Block>
|
|
182
|
+
<Block is="radio" options={radioOptions} onChange={handleChange} />
|
|
183
|
+
<Block is="accordion">Accordion content</Block>
|
|
184
|
+
<Block is="menu" trigger={<Block is="btn">Menu</Block>}>
|
|
185
|
+
Menu items
|
|
186
|
+
</Block>
|
|
187
|
+
|
|
188
|
+
// Semantic elements
|
|
189
|
+
<Block is="seo" as="nav">Navigation</Block>
|
|
190
|
+
<Block is="seo" as="main">Main content</Block>
|
|
191
|
+
<Block is="seo" as="article">Article</Block>
|
|
192
|
+
<Block is="list" as="ul">Unordered list</Block>
|
|
193
|
+
<Block is="list" as="li">List item</Block>
|
|
194
|
+
<Block is="form" as="label">Label</Block>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### The `as` Prop (Semantic HTML)
|
|
198
|
+
|
|
199
|
+
The `as` prop lets you control the underlying HTML element for SEO and accessibility:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// Text as different heading levels
|
|
203
|
+
<Block is="txt" as="h1" fontSize="2xl">Page Title</Block>
|
|
204
|
+
<Block is="txt" as="h2" fontSize="xl">Section Title</Block>
|
|
205
|
+
<Block is="txt" as="p">Paragraph</Block>
|
|
206
|
+
<Block is="txt" as="span">Inline text</Block>
|
|
207
|
+
|
|
208
|
+
// Semantic structure
|
|
209
|
+
<Block is="seo" as="header">Site header</Block>
|
|
210
|
+
<Block is="seo" as="footer">Site footer</Block>
|
|
211
|
+
<Block is="seo" as="nav">Navigation</Block>
|
|
212
|
+
<Block is="seo" as="main">Main content</Block>
|
|
213
|
+
<Block is="seo" as="aside">Sidebar</Block>
|
|
214
|
+
<Block is="seo" as="section">Section</Block>
|
|
215
|
+
|
|
216
|
+
// Lists
|
|
217
|
+
<Block is="list" as="ul">
|
|
218
|
+
<Block is="list" as="li">Item 1</Block>
|
|
219
|
+
<Block is="list" as="li">Item 2</Block>
|
|
220
|
+
</Block>
|
|
221
|
+
|
|
222
|
+
// Forms
|
|
223
|
+
<Block is="form" as="form" onSubmit={handleSubmit}>
|
|
224
|
+
<Block is="form" as="label">Email</Block>
|
|
225
|
+
<Block is="input" type="email" />
|
|
226
|
+
</Block>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Layout with Grid & Flex
|
|
230
|
+
|
|
231
|
+
#### Boolean Layout Props
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
// Flex layout
|
|
235
|
+
<Block flex>Flex container</Block>
|
|
236
|
+
<Block flex="wrap">Flex with wrapping</Block>
|
|
237
|
+
|
|
238
|
+
// Grid layout
|
|
239
|
+
<Block grid>Grid container</Block>
|
|
240
|
+
|
|
241
|
+
// Centering
|
|
242
|
+
<Block center>Centered content</Block>
|
|
243
|
+
|
|
244
|
+
// Positioning
|
|
245
|
+
<Block relative>Relative position</Block>
|
|
246
|
+
<Block absolute>Absolute position</Block>
|
|
247
|
+
<Block fixed>Fixed position</Block>
|
|
248
|
+
<Block sticky>Sticky position</Block>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### Grid `cols` Prop (Flexible Column Syntax)
|
|
252
|
+
|
|
253
|
+
The `cols` prop provides a powerful shorthand for grid columns:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
// Basic columns - numbers become fr units
|
|
257
|
+
<Block grid cols="1 1 1">Three equal columns</Block>
|
|
258
|
+
<Block grid cols="2 1 1">2fr 1fr 1fr columns</Block>
|
|
259
|
+
|
|
260
|
+
// Max-content with 'm'
|
|
261
|
+
<Block grid cols="m 1 1 m 1">
|
|
262
|
+
{/* max-content 1fr 1fr max-content 1fr */}
|
|
263
|
+
</Block>
|
|
264
|
+
|
|
265
|
+
// Alignment + columns
|
|
266
|
+
<Block grid cols="center 1 1 1">
|
|
267
|
+
{/* Centers columns: alignContent + alignItems: center */}
|
|
268
|
+
</Block>
|
|
269
|
+
<Block grid cols="start 1 2 1">
|
|
270
|
+
{/* Aligns to start */}
|
|
271
|
+
</Block>
|
|
272
|
+
|
|
273
|
+
// Column gap with slash
|
|
274
|
+
<Block grid cols="1 1 1 / 10">
|
|
275
|
+
{/* 10px column gap */}
|
|
276
|
+
</Block>
|
|
277
|
+
|
|
278
|
+
// Complete example
|
|
279
|
+
<Block grid cols="center 1 1 m 1 / 10">
|
|
280
|
+
{/* alignContent: center, alignItems: center */}
|
|
281
|
+
{/* gridTemplateColumns: 1fr 1fr max-content 1fr */}
|
|
282
|
+
{/* columnGap: 10px */}
|
|
283
|
+
</Block>
|
|
284
|
+
|
|
285
|
+
// Just alignment
|
|
286
|
+
<Block grid cols="center">
|
|
287
|
+
{/* Only centers, no column template */}
|
|
288
|
+
</Block>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### Grid `rows` Prop (Flexible Row Syntax)
|
|
292
|
+
|
|
293
|
+
The `rows` prop mirrors `cols` but for grid rows:
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
// Basic rows
|
|
297
|
+
<Block grid rows="1 1 1">Three equal rows</Block>
|
|
298
|
+
<Block grid rows="m m 1">
|
|
299
|
+
{/* max-content max-content 1fr */}
|
|
300
|
+
</Block>
|
|
301
|
+
|
|
302
|
+
// Alignment + rows
|
|
303
|
+
<Block grid rows="between m m m">
|
|
304
|
+
{/* justifyContent: space-between, justifyItems: space-between */}
|
|
305
|
+
{/* gridTemplateRows: max-content max-content max-content */}
|
|
306
|
+
</Block>
|
|
307
|
+
<Block grid rows="start 1 2 1">Start-aligned rows</Block>
|
|
308
|
+
<Block grid rows="end 1 1 1">End-aligned rows</Block>
|
|
309
|
+
|
|
310
|
+
// Row gap with slash
|
|
311
|
+
<Block grid rows="1 1 / 8">
|
|
312
|
+
{/* 8px row gap */}
|
|
313
|
+
</Block>
|
|
314
|
+
|
|
315
|
+
// Complete example
|
|
316
|
+
<Block grid rows="between m m m / 10">
|
|
317
|
+
{/* justifyContent: space-between, justifyItems: space-between */}
|
|
318
|
+
{/* gridTemplateRows: max-content max-content max-content */}
|
|
319
|
+
{/* rowGap: 10px */}
|
|
320
|
+
</Block>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### Real-World Layout Examples
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
// Chat page header
|
|
327
|
+
<Block
|
|
328
|
+
grid
|
|
329
|
+
rows="m 1 m"
|
|
330
|
+
h="100dvh"
|
|
331
|
+
w
|
|
332
|
+
maxW="700px"
|
|
333
|
+
mx="auto"
|
|
334
|
+
overflow="hidden"
|
|
335
|
+
px={{ base: 1, sm: 0 }}
|
|
336
|
+
pb={{ base: 1, sm: 4 }}
|
|
337
|
+
>
|
|
338
|
+
<Block rows="between" my={2}>
|
|
339
|
+
{/* Header content */}
|
|
340
|
+
</Block>
|
|
341
|
+
{/* Main content */}
|
|
342
|
+
{/* Footer/Chatbox */}
|
|
343
|
+
</Block>
|
|
344
|
+
|
|
345
|
+
// Playground sidebar
|
|
346
|
+
<Block grid cols="250px 1fr" h="100vh" overflow="hidden">
|
|
347
|
+
<Block
|
|
348
|
+
bg={{ base: "neutral.50", _dark: "neutral.900" }}
|
|
349
|
+
borderRight="1px solid"
|
|
350
|
+
borderColor={{ base: "neutral.200", _dark: "neutral.700" }}
|
|
351
|
+
p={4}
|
|
352
|
+
rowG={4}
|
|
353
|
+
>
|
|
354
|
+
{/* Sidebar */}
|
|
355
|
+
</Block>
|
|
356
|
+
<Block overflow="auto" position="relative">
|
|
357
|
+
{/* Main content */}
|
|
358
|
+
</Block>
|
|
359
|
+
</Block>
|
|
360
|
+
|
|
361
|
+
// Icon + text flex layout
|
|
362
|
+
<Block flex cols="center / 4">
|
|
363
|
+
<Icon icon={TbMessage2} fontSize="sm" />
|
|
364
|
+
<Block is="txt">{messageCount}</Block>
|
|
365
|
+
</Block>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Debug Utilities
|
|
369
|
+
|
|
370
|
+
The `debug` prop adds visual borders for layout debugging:
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
// Default red border
|
|
374
|
+
<Block debug>Debug border</Block>
|
|
375
|
+
|
|
376
|
+
// Color only
|
|
377
|
+
<Block debug="blue">Blue border</Block>
|
|
378
|
+
<Block debug="green">Green border</Block>
|
|
379
|
+
|
|
380
|
+
// Width + color
|
|
381
|
+
<Block debug="2px red">2px red border</Block>
|
|
382
|
+
<Block debug="10px orange">10px orange border</Block>
|
|
383
|
+
|
|
384
|
+
// Style + color
|
|
385
|
+
<Block debug="dashed blue">Dashed blue border</Block>
|
|
386
|
+
<Block debug="dotted green">Dotted green border</Block>
|
|
387
|
+
<Block debug="solid red">Solid red border</Block>
|
|
388
|
+
|
|
389
|
+
// Full control
|
|
390
|
+
<Block debug="3px dotted orange">3px dotted orange</Block>
|
|
391
|
+
<Block debug="2px dashed">2px dashed red (default color)</Block>
|
|
392
|
+
|
|
393
|
+
// Panda tokens work too
|
|
394
|
+
<Block debug="blue.500">Uses Panda color token</Block>
|
|
395
|
+
<Block debug="text-primary">Uses semantic token</Block>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Compound Components
|
|
399
|
+
|
|
400
|
+
Block includes several compound components accessible via namespace:
|
|
401
|
+
|
|
402
|
+
#### Progress
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
// Simple progress
|
|
406
|
+
<Block is="progress" value={50} max={100} w="200px" h={6} />
|
|
407
|
+
|
|
408
|
+
// Custom styled progress
|
|
409
|
+
<Block is="progress" value={progress} max={100} w="200px" h={6}>
|
|
410
|
+
<Block.Progress.TrackBg
|
|
411
|
+
bg={{ base: "red.200", _dark: "neutral.700" }}
|
|
412
|
+
/>
|
|
413
|
+
<Block.Progress.TrackFill
|
|
414
|
+
bg={{
|
|
415
|
+
base: "blue.500",
|
|
416
|
+
_dark: progress > 60 ? "green.400" : "red.400",
|
|
417
|
+
}}
|
|
418
|
+
/>
|
|
419
|
+
</Block>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### Drawer
|
|
423
|
+
|
|
424
|
+
```tsx
|
|
425
|
+
<Block is="btn" onClick={() => setOpen(true)}>
|
|
426
|
+
Open Drawer
|
|
427
|
+
</Block>
|
|
428
|
+
|
|
429
|
+
<Block is="drawer" open={isOpen} onClose={() => setOpen(false)}>
|
|
430
|
+
<Block.Drawer.CloseButton onClick={() => setOpen(false)} />
|
|
431
|
+
<Block p={8}>
|
|
432
|
+
Drawer content here
|
|
433
|
+
</Block>
|
|
434
|
+
</Block>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### Radio
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
// Default radio
|
|
441
|
+
<Block
|
|
442
|
+
is="radio"
|
|
443
|
+
defaultValue="1"
|
|
444
|
+
options={[
|
|
445
|
+
{ label: "Option 1", value: "1" },
|
|
446
|
+
{ label: "Option 2", value: "2" },
|
|
447
|
+
{ label: "Option 3 (disabled)", value: "3", disabled: true },
|
|
448
|
+
]}
|
|
449
|
+
onChange={(value) => console.log(value)}
|
|
450
|
+
_radioSelected={{ _radioCircleOuter: { borderColor: "blue.400" } }}
|
|
451
|
+
/>
|
|
452
|
+
|
|
453
|
+
// Custom rendered radio
|
|
454
|
+
<Block
|
|
455
|
+
is="radio"
|
|
456
|
+
defaultValue="2"
|
|
457
|
+
options={options}
|
|
458
|
+
_radioCircleOuter={{ size: 12, borderColor: "neutral.500" }}
|
|
459
|
+
_radioCircleInner={{ size: 4 }}
|
|
460
|
+
_radioSelected={{ _radioCircleOuter: { borderColor: "violet.400" } }}
|
|
461
|
+
_radioDisabled={{ _radioCircleOuter: { borderColor: "neutral.400" } }}
|
|
462
|
+
renderOption={(option) => (
|
|
463
|
+
<Block.Radio.Item
|
|
464
|
+
cols="/ 10"
|
|
465
|
+
value={option.value}
|
|
466
|
+
disabled={option.disabled}
|
|
467
|
+
>
|
|
468
|
+
{option.label}
|
|
469
|
+
</Block.Radio.Item>
|
|
470
|
+
)}
|
|
471
|
+
onChange={(value) => console.log(value)}
|
|
472
|
+
/>
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### Switch
|
|
476
|
+
|
|
477
|
+
```tsx
|
|
478
|
+
<Block is="switch" checked={isOn} onChange={setIsOn}>
|
|
479
|
+
<Block.Switch.Track
|
|
480
|
+
bg={{
|
|
481
|
+
base: isOn ? "blue.500!" : "neutral.300",
|
|
482
|
+
_dark: isOn ? "blue.500!" : "neutral.700",
|
|
483
|
+
}}
|
|
484
|
+
/>
|
|
485
|
+
<Block.Switch.Thumb bg={{ base: "neutral.100", _dark: "neutral.900" }} />
|
|
486
|
+
</Block>
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### Accordion
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
<Block is="accordion">
|
|
493
|
+
<Block.Accordion.Item title="Accordion Item 1" cols="/ 10">
|
|
494
|
+
Content for item 1
|
|
495
|
+
</Block.Accordion.Item>
|
|
496
|
+
<Block.Accordion.Item
|
|
497
|
+
title={
|
|
498
|
+
<Block is="txt" color="text-primary">
|
|
499
|
+
Custom Title
|
|
500
|
+
</Block>
|
|
501
|
+
}
|
|
502
|
+
cols="/ 10"
|
|
503
|
+
>
|
|
504
|
+
Content for item 2
|
|
505
|
+
</Block.Accordion.Item>
|
|
506
|
+
</Block>
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### Input
|
|
510
|
+
|
|
511
|
+
```tsx
|
|
512
|
+
// Simple input
|
|
513
|
+
<Block is="input" placeholder="Search" value={value} onChange={handleChange} />
|
|
514
|
+
|
|
515
|
+
// Input with icon and label
|
|
516
|
+
<Block
|
|
517
|
+
is="input"
|
|
518
|
+
placeholder="Search tests"
|
|
519
|
+
pl="28px"
|
|
520
|
+
value={searchTerm}
|
|
521
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
522
|
+
>
|
|
523
|
+
<Block.Input.LeftIcon
|
|
524
|
+
icon="search"
|
|
525
|
+
left="8px"
|
|
526
|
+
fontSize="xs"
|
|
527
|
+
color="text-secondary"
|
|
528
|
+
/>
|
|
529
|
+
</Block>
|
|
530
|
+
|
|
531
|
+
// Input with label
|
|
532
|
+
<Block is="input" placeholder="Email">
|
|
533
|
+
<Block.Input.Label>Email Address</Block.Input.Label>
|
|
534
|
+
</Block>
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### Textarea
|
|
538
|
+
|
|
539
|
+
```tsx
|
|
540
|
+
<Block
|
|
541
|
+
is="textarea"
|
|
542
|
+
placeholder="Type something..."
|
|
543
|
+
pl="28px"
|
|
544
|
+
error="This is an error"
|
|
545
|
+
minW={200}
|
|
546
|
+
minRows={2}
|
|
547
|
+
maxRows={8}
|
|
548
|
+
transition=".2s height ease-out"
|
|
549
|
+
value={txtarea}
|
|
550
|
+
onChange={(e) => setTxtareaVal(e.target.value)}
|
|
551
|
+
>
|
|
552
|
+
<Block.Textarea.Label>Description</Block.Textarea.Label>
|
|
553
|
+
</Block>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### Menu
|
|
557
|
+
|
|
558
|
+
```tsx
|
|
559
|
+
<Block is="menu" variant="click" trigger={<Block is="btn">Open Menu</Block>}>
|
|
560
|
+
<Block.Menu.Container grid rows="/ 4" px={4} py={2} borderRadius="8">
|
|
561
|
+
<Block is="txt" color="text-primary">
|
|
562
|
+
Menu Item 1
|
|
563
|
+
</Block>
|
|
564
|
+
<Block is="txt" color="text-primary">
|
|
565
|
+
Menu Item 2
|
|
566
|
+
</Block>
|
|
567
|
+
<Block is="txt" color="text-primary">
|
|
568
|
+
Menu Item 3
|
|
569
|
+
</Block>
|
|
570
|
+
</Block.Menu.Container>
|
|
571
|
+
</Block>
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
#### Tooltip
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
// Simple tooltip
|
|
578
|
+
<Block is="btn" tooltip="This is a tooltip">
|
|
579
|
+
Hover me
|
|
580
|
+
</Block>
|
|
581
|
+
|
|
582
|
+
// Custom tooltip
|
|
583
|
+
<Block
|
|
584
|
+
is="btn"
|
|
585
|
+
tooltip={
|
|
586
|
+
<Block.Tooltip.Container
|
|
587
|
+
bg="blue.900"
|
|
588
|
+
color={{ base: "blue.400", _dark: "red.400" }}
|
|
589
|
+
>
|
|
590
|
+
Custom styled tooltip!
|
|
591
|
+
</Block.Tooltip.Container>
|
|
592
|
+
}
|
|
593
|
+
>
|
|
594
|
+
Hover for custom tooltip
|
|
595
|
+
</Block>
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Common Utility Props
|
|
599
|
+
|
|
600
|
+
```tsx
|
|
601
|
+
// Sizing
|
|
602
|
+
<Block w>Full width (100%)</Block>
|
|
603
|
+
<Block w={200}>200px width</Block>
|
|
604
|
+
<Block h>Full height (100%)</Block>
|
|
605
|
+
<Block size={40}>40px × 40px</Block>
|
|
606
|
+
|
|
607
|
+
// Spacing
|
|
608
|
+
<Block p={4}>Padding 4</Block>
|
|
609
|
+
<Block px={2} py={1}>Padding x/y</Block>
|
|
610
|
+
<Block m={2}>Margin 2</Block>
|
|
611
|
+
<Block mx="auto">Centered horizontally</Block>
|
|
612
|
+
<Block gap={4}>Gap for flex/grid children</Block>
|
|
613
|
+
<Block rowG={3}>Row gap (grid)</Block>
|
|
614
|
+
<Block colG={3}>Column gap (grid)</Block>
|
|
615
|
+
|
|
616
|
+
// Borders
|
|
617
|
+
<Block br={4}>Border radius 4px</Block>
|
|
618
|
+
<Block rounded>Fully rounded (9999px)</Block>
|
|
619
|
+
<Block border="1px solid" borderColor="neutral.300">
|
|
620
|
+
Custom border
|
|
621
|
+
</Block>
|
|
622
|
+
|
|
623
|
+
// Colors
|
|
624
|
+
<Block bg="blue.500">Background color</Block>
|
|
625
|
+
<Block color="text-primary">Text color</Block>
|
|
626
|
+
|
|
627
|
+
// Typography
|
|
628
|
+
<Block fontSize="xl">Extra large text</Block>
|
|
629
|
+
<Block fontWeight="bold">Bold text</Block>
|
|
630
|
+
<Block letterSpacing={0.2}>Letter spacing</Block>
|
|
631
|
+
|
|
632
|
+
// Display
|
|
633
|
+
<Block overflow="hidden">Hidden overflow</Block>
|
|
634
|
+
<Block overflowY="auto">Vertical scroll</Block>
|
|
635
|
+
<Block opacity={0.5}>50% opacity</Block>
|
|
636
|
+
|
|
637
|
+
// Positioning
|
|
638
|
+
<Block zIndex={10}>Z-index 10</Block>
|
|
639
|
+
<Block top={0} left={0}>Positioned</Block>
|
|
640
|
+
|
|
641
|
+
// Interactions
|
|
642
|
+
<Block pointer>Cursor pointer</Block>
|
|
643
|
+
<Block cursor="not-allowed">Disabled cursor</Block>
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### Icon Support
|
|
647
|
+
|
|
648
|
+
```tsx
|
|
649
|
+
// Standalone icon
|
|
650
|
+
<Block icon="check-circle" color="green.500" />
|
|
651
|
+
|
|
652
|
+
// Icon with text
|
|
653
|
+
<Block is="btn" icon="search" fontSize="sm">
|
|
654
|
+
Search
|
|
655
|
+
</Block>
|
|
656
|
+
|
|
657
|
+
// Start and end icons
|
|
658
|
+
<Block is="btn" startIcon="arrow-left" endIcon="arrow-right">
|
|
659
|
+
Navigate
|
|
660
|
+
</Block>
|
|
661
|
+
|
|
662
|
+
// Loading state
|
|
663
|
+
<Block is="btn" isLoading loadingIcon="oval">
|
|
664
|
+
Loading
|
|
665
|
+
</Block>
|
|
666
|
+
|
|
667
|
+
// Custom loading styles
|
|
668
|
+
<Block
|
|
669
|
+
is="btn"
|
|
670
|
+
isLoading
|
|
671
|
+
_loading={{ strokeColor: "blue.500", opacity: 0.8 }}
|
|
672
|
+
>
|
|
673
|
+
Processing
|
|
674
|
+
</Block>
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Special Features
|
|
678
|
+
|
|
679
|
+
#### Skeleton Loading
|
|
680
|
+
|
|
681
|
+
```tsx
|
|
682
|
+
// Circle skeleton
|
|
683
|
+
<Block is="skeleton" as="circle" />
|
|
684
|
+
|
|
685
|
+
// Block skeleton
|
|
686
|
+
<Block is="skeleton" as="block" h={8} br={4} />
|
|
687
|
+
<Block is="skeleton" as="block" h={8} br={4} w="85%" />
|
|
688
|
+
|
|
689
|
+
// Skeleton layout example
|
|
690
|
+
<Block grid rows="/ 18" w>
|
|
691
|
+
<Block flex cols="center / 24">
|
|
692
|
+
<Block is="skeleton" as="circle" />
|
|
693
|
+
<Block grid rows="/ 18" w>
|
|
694
|
+
<Block is="skeleton" as="block" h={8} br={4} />
|
|
695
|
+
<Block is="skeleton" as="block" h={8} br={4} />
|
|
696
|
+
</Block>
|
|
697
|
+
</Block>
|
|
698
|
+
<Block is="skeleton" as="block" h={8} br={4} w="85%" />
|
|
699
|
+
</Block>
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
#### Horizontal Rule
|
|
703
|
+
|
|
704
|
+
```tsx
|
|
705
|
+
<Block is="hr" />
|
|
706
|
+
<Block is="hr" color="blue.500" />
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
#### File Picker
|
|
710
|
+
|
|
711
|
+
```tsx
|
|
712
|
+
<Block is="btn" filepicker multiple onChange={(files) => handleFiles(files)}>
|
|
713
|
+
Choose Files
|
|
714
|
+
</Block>
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
#### Portal Rendering
|
|
718
|
+
|
|
719
|
+
```tsx
|
|
720
|
+
<Block portal>Rendered in portal</Block>
|
|
721
|
+
<Block portal portalContainer={customContainer}>
|
|
722
|
+
Custom portal target
|
|
723
|
+
</Block>
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Kitchen Sink Example
|
|
727
|
+
|
|
728
|
+
```tsx
|
|
729
|
+
// Complex button with all features
|
|
730
|
+
<Block
|
|
731
|
+
is="btn"
|
|
732
|
+
grid
|
|
733
|
+
cols="m m / 8"
|
|
734
|
+
rows="center"
|
|
735
|
+
px={4}
|
|
736
|
+
py={2}
|
|
737
|
+
bg={{ base: "blue.500", _dark: "blue.600" }}
|
|
738
|
+
color="white"
|
|
739
|
+
rounded
|
|
740
|
+
icon="check-circle"
|
|
741
|
+
isLoading={isProcessing}
|
|
742
|
+
disabled={!isValid}
|
|
743
|
+
loadingIcon="spinner"
|
|
744
|
+
_loading={{ strokeColor: "white", opacity: 0.6 }}
|
|
745
|
+
_hover={{ bg: "blue.600" }}
|
|
746
|
+
onClick={handleSubmit}
|
|
747
|
+
whileTap={{ scale: 0.95 }}
|
|
748
|
+
transition={{ duration: 0.2 }}
|
|
749
|
+
tooltip="Submit form"
|
|
750
|
+
debug="dashed blue"
|
|
751
|
+
>
|
|
752
|
+
Submit Form
|
|
753
|
+
</Block>
|
|
754
|
+
|
|
755
|
+
// Complex layout composition
|
|
756
|
+
<Block
|
|
757
|
+
grid
|
|
758
|
+
rows="m 1 m"
|
|
759
|
+
h="100vh"
|
|
760
|
+
w
|
|
761
|
+
maxW="1200px"
|
|
762
|
+
mx="auto"
|
|
763
|
+
bg={{ base: "neutral.50", _dark: "neutral.900" }}
|
|
764
|
+
borderRadius={12}
|
|
765
|
+
p={6}
|
|
766
|
+
gap={4}
|
|
767
|
+
>
|
|
768
|
+
{/* Header */}
|
|
769
|
+
<Block
|
|
770
|
+
is="seo"
|
|
771
|
+
as="header"
|
|
772
|
+
grid
|
|
773
|
+
cols="1 m / 12"
|
|
774
|
+
alignI="center"
|
|
775
|
+
borderBottom="1px solid"
|
|
776
|
+
borderColor="neutral.200"
|
|
777
|
+
pb={4}
|
|
778
|
+
>
|
|
779
|
+
<Block is="txt" as="h1" fontSize="2xl" fontWeight="bold">
|
|
780
|
+
Dashboard
|
|
781
|
+
</Block>
|
|
782
|
+
<Block is="btn" icon="settings" variant="ghost">
|
|
783
|
+
Settings
|
|
784
|
+
</Block>
|
|
785
|
+
</Block>
|
|
786
|
+
|
|
787
|
+
{/* Main Content */}
|
|
788
|
+
<Block
|
|
789
|
+
is="seo"
|
|
790
|
+
as="main"
|
|
791
|
+
grid
|
|
792
|
+
rows="/ 24"
|
|
793
|
+
overflow="auto"
|
|
794
|
+
>
|
|
795
|
+
<Block
|
|
796
|
+
grid
|
|
797
|
+
cols="1 1 1 / 16"
|
|
798
|
+
rows="/ 16"
|
|
799
|
+
bg="white"
|
|
800
|
+
br={8}
|
|
801
|
+
p={4}
|
|
802
|
+
debug="neutral.200"
|
|
803
|
+
>
|
|
804
|
+
<Block
|
|
805
|
+
is="progress"
|
|
806
|
+
value={progress}
|
|
807
|
+
max={100}
|
|
808
|
+
h={6}
|
|
809
|
+
>
|
|
810
|
+
<Block.Progress.TrackFill bg="green.500" />
|
|
811
|
+
</Block>
|
|
812
|
+
{/* More content */}
|
|
813
|
+
</Block>
|
|
814
|
+
</Block>
|
|
815
|
+
|
|
816
|
+
{/* Footer */}
|
|
817
|
+
<Block
|
|
818
|
+
is="seo"
|
|
819
|
+
as="footer"
|
|
820
|
+
flex
|
|
821
|
+
cols="center / 8"
|
|
822
|
+
borderTop="1px solid"
|
|
823
|
+
borderColor="neutral.200"
|
|
824
|
+
pt={4}
|
|
825
|
+
>
|
|
826
|
+
<Block is="txt" fontSize="xs" color="text-secondary">
|
|
827
|
+
© 2025 Your Company
|
|
828
|
+
</Block>
|
|
829
|
+
</Block>
|
|
830
|
+
</Block>
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
### Responsive Design
|
|
834
|
+
|
|
835
|
+
All props support Panda's responsive syntax:
|
|
836
|
+
|
|
837
|
+
```tsx
|
|
838
|
+
<Block
|
|
839
|
+
grid
|
|
840
|
+
cols={{ base: "1", md: "1 1", lg: "1 1 1" }}
|
|
841
|
+
gap={{ base: 4, md: 6, lg: 8 }}
|
|
842
|
+
p={{ base: 2, sm: 4, md: 6 }}
|
|
843
|
+
fontSize={{ base: "sm", md: "md", lg: "lg" }}
|
|
844
|
+
>
|
|
845
|
+
Responsive content
|
|
846
|
+
</Block>
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### Theme Awareness
|
|
850
|
+
|
|
851
|
+
Block automatically supports light/dark themes:
|
|
852
|
+
|
|
853
|
+
```tsx
|
|
854
|
+
<Block
|
|
855
|
+
bg={{ base: "white", _dark: "neutral.900" }}
|
|
856
|
+
color={{ base: "neutral.900", _dark: "neutral.100" }}
|
|
857
|
+
borderColor={{ base: "neutral.200", _dark: "neutral.700" }}
|
|
858
|
+
>
|
|
859
|
+
Theme-aware content
|
|
860
|
+
</Block>
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### TypeScript Support
|
|
864
|
+
|
|
865
|
+
Block is fully typed with TypeScript:
|
|
866
|
+
|
|
867
|
+
```tsx
|
|
868
|
+
// Type-safe is/as combinations
|
|
869
|
+
<Block is="txt" as="h1">Title</Block> // ✅
|
|
870
|
+
<Block is="list" as="ul">List</Block> // ✅
|
|
871
|
+
<Block is="seo" as="nav">Nav</Block> // ✅
|
|
872
|
+
|
|
873
|
+
// Compile-time errors for invalid combinations
|
|
874
|
+
<Block is="txt" as="ul">Invalid</Block> // ❌ Error
|
|
875
|
+
<Block is="list">Missing as</Block> // ❌ Error
|
|
876
|
+
|
|
877
|
+
// Proper event typing
|
|
878
|
+
<Block is="btn" onClick={(e: React.MouseEvent) => {}}>
|
|
879
|
+
Typed events
|
|
880
|
+
</Block>
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Best Practices
|
|
884
|
+
|
|
885
|
+
1. **Use semantic HTML**: Always provide the `as` prop for proper SEO and accessibility
|
|
886
|
+
2. **Prefer grid/flex**: Use the boolean `grid` and `flex` props over manual CSS
|
|
887
|
+
3. **Leverage cols/rows**: The shorthand syntax is more readable than verbose gridTemplateColumns
|
|
888
|
+
4. **Debug visually**: Use `debug` prop during development to understand layouts
|
|
889
|
+
5. **Compound components**: Use Block compound components instead of mixing component types
|
|
890
|
+
6. **Responsive by default**: Always consider mobile-first responsive design
|
|
891
|
+
7. **Type safety**: Let TypeScript guide you to correct `is`/`as` combinations
|
|
124
892
|
|
|
125
893
|
## License
|
|
126
894
|
|
package/dist/preset/index.cjs
CHANGED
|
@@ -4,7 +4,27 @@
|
|
|
4
4
|
var preset = {
|
|
5
5
|
name: "qstd-preset",
|
|
6
6
|
globalCss: {
|
|
7
|
-
":root": {
|
|
7
|
+
":root": {
|
|
8
|
+
/**
|
|
9
|
+
* Default UA surfaces (forms, scrollbars, etc.) to light mode so
|
|
10
|
+
* projects that do not opt into dark mode still get consistent styling.
|
|
11
|
+
*/
|
|
12
|
+
colorScheme: "light"
|
|
13
|
+
},
|
|
14
|
+
"html[data-theme=light]": {
|
|
15
|
+
/**
|
|
16
|
+
* Keep the UA elements in sync with Panda's light theme tokens whenever
|
|
17
|
+
* a consumer sets data-theme="light".
|
|
18
|
+
*/
|
|
19
|
+
colorScheme: "light"
|
|
20
|
+
},
|
|
21
|
+
"html[data-theme=dark]": {
|
|
22
|
+
/**
|
|
23
|
+
* Propagate the dark theme toggle down to the browser chrome so native
|
|
24
|
+
* controls respect the chosen theme.
|
|
25
|
+
*/
|
|
26
|
+
colorScheme: "dark"
|
|
27
|
+
},
|
|
8
28
|
html: {
|
|
9
29
|
fontSize: 16,
|
|
10
30
|
lineHeight: 1.5,
|
package/dist/preset/index.js
CHANGED
|
@@ -2,7 +2,27 @@
|
|
|
2
2
|
var preset = {
|
|
3
3
|
name: "qstd-preset",
|
|
4
4
|
globalCss: {
|
|
5
|
-
":root": {
|
|
5
|
+
":root": {
|
|
6
|
+
/**
|
|
7
|
+
* Default UA surfaces (forms, scrollbars, etc.) to light mode so
|
|
8
|
+
* projects that do not opt into dark mode still get consistent styling.
|
|
9
|
+
*/
|
|
10
|
+
colorScheme: "light"
|
|
11
|
+
},
|
|
12
|
+
"html[data-theme=light]": {
|
|
13
|
+
/**
|
|
14
|
+
* Keep the UA elements in sync with Panda's light theme tokens whenever
|
|
15
|
+
* a consumer sets data-theme="light".
|
|
16
|
+
*/
|
|
17
|
+
colorScheme: "light"
|
|
18
|
+
},
|
|
19
|
+
"html[data-theme=dark]": {
|
|
20
|
+
/**
|
|
21
|
+
* Propagate the dark theme toggle down to the browser chrome so native
|
|
22
|
+
* controls respect the chosen theme.
|
|
23
|
+
*/
|
|
24
|
+
colorScheme: "dark"
|
|
25
|
+
},
|
|
6
26
|
html: {
|
|
7
27
|
fontSize: 16,
|
|
8
28
|
lineHeight: 1.5,
|
package/dist/react/index.css
CHANGED
|
@@ -205,7 +205,13 @@
|
|
|
205
205
|
@layer base {
|
|
206
206
|
:root {
|
|
207
207
|
--made-with-panda: "\1f43c";
|
|
208
|
-
|
|
208
|
+
}
|
|
209
|
+
:root,
|
|
210
|
+
html[data-theme=light] {
|
|
211
|
+
color-scheme: light;
|
|
212
|
+
}
|
|
213
|
+
html[data-theme=dark] {
|
|
214
|
+
color-scheme: dark;
|
|
209
215
|
}
|
|
210
216
|
html {
|
|
211
217
|
font-size: 16px;
|
package/dist/react/index.d.cts
CHANGED
|
@@ -20956,7 +20956,7 @@ declare function RightSide(props: BaseBlockProps & {
|
|
|
20956
20956
|
declare namespace RightSide {
|
|
20957
20957
|
var displayName: string;
|
|
20958
20958
|
}
|
|
20959
|
-
declare function Label(props: InputBlockProps & LabelProps): react_jsx_runtime.JSX.Element;
|
|
20959
|
+
declare function Label(props: Omit<InputBlockProps, "is"> & LabelProps): react_jsx_runtime.JSX.Element;
|
|
20960
20960
|
declare namespace Label {
|
|
20961
20961
|
var displayName: string;
|
|
20962
20962
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -20956,7 +20956,7 @@ declare function RightSide(props: BaseBlockProps & {
|
|
|
20956
20956
|
declare namespace RightSide {
|
|
20957
20957
|
var displayName: string;
|
|
20958
20958
|
}
|
|
20959
|
-
declare function Label(props: InputBlockProps & LabelProps): react_jsx_runtime.JSX.Element;
|
|
20959
|
+
declare function Label(props: Omit<InputBlockProps, "is"> & LabelProps): react_jsx_runtime.JSX.Element;
|
|
20960
20960
|
declare namespace Label {
|
|
20961
20961
|
var displayName: string;
|
|
20962
20962
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qstd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Standard Block component and utilities library with Panda CSS",
|
|
5
5
|
"author": "malin1",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,21 @@
|
|
|
41
41
|
"sideEffects": [
|
|
42
42
|
"dist/react/index.css"
|
|
43
43
|
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "panda codegen && panda cssgen && tsup && node scripts/inject-css-import.js",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"prepublishOnly": "pnpm build",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"test:all": "pnpx tsx tests/test-all.ts",
|
|
51
|
+
"test:block": "node tests/test-block.tsx",
|
|
52
|
+
"test:playground": "node tests/playground-e2e.mjs",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"typecheck:perf": "tsc --noEmit --extendedDiagnostics",
|
|
55
|
+
"typecheck:trace": "tsc --noEmit --generateTrace ./performance/ts-trace",
|
|
56
|
+
"analyze:tsserver": "bash performance/analyze-tsserver.sh",
|
|
57
|
+
"lint": "eslint src --ext ts,tsx"
|
|
58
|
+
},
|
|
44
59
|
"peerDependencies": {
|
|
45
60
|
"react": "^18.0.0 || ^19.0.0",
|
|
46
61
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
@@ -93,19 +108,5 @@
|
|
|
93
108
|
"bugs": {
|
|
94
109
|
"url": "https://github.com/55cancri/qstd/issues"
|
|
95
110
|
},
|
|
96
|
-
"homepage": "https://github.com/55cancri/qstd#readme"
|
|
97
|
-
|
|
98
|
-
"build": "panda codegen && panda cssgen && tsup && node scripts/inject-css-import.js",
|
|
99
|
-
"dev": "tsup --watch",
|
|
100
|
-
"test": "vitest run",
|
|
101
|
-
"test:watch": "vitest",
|
|
102
|
-
"test:all": "pnpx tsx tests/test-all.ts",
|
|
103
|
-
"test:block": "node tests/test-block.tsx",
|
|
104
|
-
"test:playground": "node tests/playground-e2e.mjs",
|
|
105
|
-
"typecheck": "tsc --noEmit",
|
|
106
|
-
"typecheck:perf": "tsc --noEmit --extendedDiagnostics",
|
|
107
|
-
"typecheck:trace": "tsc --noEmit --generateTrace ./performance/ts-trace",
|
|
108
|
-
"analyze:tsserver": "bash performance/analyze-tsserver.sh",
|
|
109
|
-
"lint": "eslint src --ext ts,tsx"
|
|
110
|
-
}
|
|
111
|
-
}
|
|
111
|
+
"homepage": "https://github.com/55cancri/qstd#readme"
|
|
112
|
+
}
|