b44ui 0.0.11 → 0.0.13

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.
Files changed (4) hide show
  1. package/index.tsx +136 -34
  2. package/package.json +1 -1
  3. package/readme.md +9 -4
  4. package/styles.css +10 -11
package/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { type ReactNode, Children, useMemo, useState } from "react"
1
+ import { type ReactNode, Children, useMemo, useRef, useState } from "react"
2
2
  import { Marked } from "marked"
3
3
  import { markedHighlight } from "marked-highlight"
4
4
  import hljs from "highlight.js"
@@ -6,13 +6,14 @@ import type { ClassValue } from "clsx"
6
6
  import clsx from "clsx"
7
7
  import { twMerge } from "tailwind-merge"
8
8
 
9
- export type Color = 'red' | 'blue' | 'orange' | 'purple' | 'yellow'
9
+ export type Color = 'red' | 'blue' | 'orange' | 'purple' | 'yellow' | 'green'
10
10
  const tintMap: Record<Color, string> = {
11
11
  red: 'bg-red-500/20 border-red-500',
12
12
  blue: 'bg-blue-500/20 border-blue-500',
13
13
  orange: 'bg-orange-500/20 border-orange-500',
14
14
  purple: 'bg-purple-500/20 border-purple-500',
15
15
  yellow: 'bg-yellow-500/20 border-yellow-500',
16
+ green: 'bg-green-500/20 border-green-500',
16
17
  }
17
18
  const colorMap: Record<Color, string> = {
18
19
  red: 'bg-red-600 hover:bg-red-500',
@@ -20,6 +21,7 @@ const colorMap: Record<Color, string> = {
20
21
  orange: 'bg-orange-600 hover:bg-orange-500',
21
22
  purple: 'bg-purple-600 hover:bg-purple-500',
22
23
  yellow: 'bg-yellow-600 hover:bg-yellow-500',
24
+ green: 'bg-green-600 hover:bg-green-500',
23
25
  }
24
26
  const tintCn = (c: Color) => tintMap[c]
25
27
  const colorCn = (c: Color) => colorMap[c]
@@ -39,12 +41,14 @@ export type DProps = {
39
41
  grow?: boolean
40
42
  gap?: number
41
43
  p?: number
44
+ wd?: number
45
+ ht?: number
42
46
  }
43
47
 
44
- const dStyle = ({ gap, p, style }: DProps): React.CSSProperties | undefined => {
48
+ const dStyle = ({ gap, p, wd, ht, style }: DProps): React.CSSProperties | undefined => {
45
49
  const g = sc(gap), pd = sc(p)
46
- if (!g && !pd && !style) return undefined
47
- return { ...(g !== undefined && { gap: g }), ...(pd !== undefined && { padding: pd }), ...style }
50
+ if (!g && !pd && wd === undefined && ht === undefined && !style) return undefined
51
+ return { ...(g !== undefined && { gap: g }), ...(pd !== undefined && { padding: pd }), ...(wd !== undefined && { width: `${wd * 100}%` }), ...(ht !== undefined && { height: `${ht * 100}%` }), ...style }
48
52
  }
49
53
 
50
54
  export const D = (props: DProps) =>
@@ -60,39 +64,57 @@ export const App = (props: DProps & { center?: boolean, width?: number }) => {
60
64
  }
61
65
 
62
66
  export const Centered = (props: DProps & { width?: number }) => {
63
- const { children, grow, gap, p, width = 700 } = props
64
- return <D cn={CN("mx-auto max-w-full px-6", rcn(props))} grow={grow} gap={gap} p={p} style={{ width }}>{children}</D>
67
+ const { children, grow, gap, p, wd, ht, width = 700 } = props
68
+ return <D cn={CN("mx-auto max-w-full px-6", rcn(props))} grow={grow} gap={gap} p={p} wd={wd} ht={ht} style={{ width }}>{children}</D>
69
+ }
70
+
71
+ export const TabList = (props: DProps) => {
72
+ const { children, grow, gap, p, wd, ht } = props
73
+ return <D cn={CN("flex items-end gap-4", rcn(props))} grow={grow} gap={gap} p={p} wd={wd} ht={ht}>{children}</D>
74
+ }
75
+
76
+ export const Tab = ({ title, active, click, grow, gap, p, ...rest }: DProps & { title: ReactNode, active?: boolean, click?: () => void } & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
77
+ const c = rcn(rest as DProps)
78
+ return <button
79
+ className={CN("cursor-pointer border-b-2 pb-1.5 text-sm font-medium", active ? "border-zinc-100 text-zinc-100" : "border-transparent text-zinc-400 hover:text-zinc-200", grow && "flex-1", c)}
80
+ style={dStyle({ gap, p, wd: rest.wd, style: rest.style })}
81
+ onClick={click}
82
+ role="tab"
83
+ aria-selected={active}
84
+ {...rest}
85
+ >{title}</button>
65
86
  }
66
87
 
67
88
  export const Block = (props: DProps & { label?: ReactNode, row?: boolean, dashed?: boolean }) => {
68
- const { label, children, row, dashed, grow, gap, p } = props
69
- return <D cn={CN("rounded flex flex-col items-center", dashed && "border border-dashed border-zinc-700", rcn(props))} grow={grow} gap={gap ?? 4} p={p ?? 4}>
89
+ const { label, children, row, dashed, grow, gap, p, wd, ht } = props
90
+ return <D cn={CN("rounded flex flex-col items-center", dashed && "border border-dashed border-zinc-700", rcn(props))} grow={grow} gap={gap ?? 4} p={p ?? 4} wd={wd} ht={ht}>
70
91
  {label && <span className="opacity-75">{label}</span>}
71
92
  <div className={CN(row ? "flex-row" : "flex-col", "flex items-center")} style={{ gap: sc(gap ?? 4) }}>{children}</div>
72
93
  </D>
73
94
  }
74
95
 
75
96
  export const BlockSm = (props: DProps & { dashed?: boolean }) => {
76
- const { children, dashed, grow, gap, p } = props
77
- return <D cn={CN("rounded text-sm", dashed && "border border-dashed border-zinc-700", rcn(props))} grow={grow} gap={gap} p={p ?? 2}
97
+ const { children, dashed, grow, gap, p, wd, ht } = props
98
+ return <D cn={CN("rounded text-sm", dashed && "border border-dashed border-zinc-700", rcn(props))} grow={grow} gap={gap} p={p ?? 2} wd={wd} ht={ht}
78
99
  style={{ paddingLeft: sc(3), paddingRight: sc(3) }}>{children}</D>
79
100
  }
80
101
 
81
102
  export const Chip = (props: DProps & { click?: () => void }) => {
82
- const { children, click } = props
103
+ const { children, click, grow, gap, p, wd, ht, style } = props
83
104
  return <span className={CN("bg-zinc-800 rounded px-2 py-0.5 text-xs", click && "cursor-pointer hover:bg-zinc-700", rcn(props))}
105
+ style={dStyle({ gap, p, wd, ht, style })}
84
106
  onClick={click}>{children}</span>
85
107
  }
86
108
 
87
109
  export const Card = (props: DProps) => {
88
- const { children, grow, gap, p } = props
89
- return <D cn={CN("rounded border border-zinc-700 bg-zinc-900", rcn(props))} grow={grow} gap={gap ?? 4} p={p ?? 4}
110
+ const { children, grow, gap, p, wd, ht } = props
111
+ return <D cn={CN("rounded border border-zinc-700 bg-zinc-900", rcn(props))} grow={grow} gap={gap ?? 4} p={p ?? 4} wd={wd} ht={ht}
90
112
  style={{ display: 'flex', flexDirection: 'column' }}>{children}</D>
91
113
  }
92
114
 
93
- export const Col = (props: DProps) => {
94
- const { children, grow, gap, p } = props
95
- return <D cn={CN("flex flex-col", rcn(props))} grow={grow} gap={gap ?? 4} p={p}>{children}</D>
115
+ export const Col = (props: DProps & { align?: 'start' | 'center' | 'end' }) => {
116
+ const { children, grow, gap, p, wd, ht, align } = props
117
+ return <D cn={CN("flex flex-col", align === 'start' && 'items-start', align === 'center' && 'items-center', align === 'end' && 'items-end', rcn(props))} grow={grow} gap={gap ?? 4} p={p} wd={wd} ht={ht}>{children}</D>
96
118
  }
97
119
 
98
120
  export const Popover = (props: DProps & { text: ReactNode, color?: Color }) => {
@@ -113,7 +135,7 @@ export const Muted = (props: DProps) => {
113
135
  }
114
136
 
115
137
  export const A = ({
116
- children, cn, cnIgnoreWrongUsage, grow, gap, p, style, click, href, onClick, onKeyDown, role, tabIndex, ...rest
138
+ children, cn, cnIgnoreWrongUsage, grow, gap, p, wd, style, click, href, onClick, onKeyDown, role, tabIndex, ...rest
117
139
  }: DProps & { click?: () => void } & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
118
140
  const c = rcn({ cn, cnIgnoreWrongUsage })
119
141
  const fire = (e: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>) => {
@@ -128,7 +150,7 @@ export const A = ({
128
150
  role={buttonLike ? role ?? 'button' : role}
129
151
  tabIndex={buttonLike ? tabIndex ?? 0 : tabIndex}
130
152
  className={CN("inline-flex items-center gap-1 text-zinc-300 underline underline-offset-4 cursor-pointer hover:text-zinc-100", grow && "flex-1", c)}
131
- style={dStyle({ gap, p, style })}
153
+ style={dStyle({ gap, p, wd, style })}
132
154
  onClick={fire}
133
155
  onKeyDown={e => {
134
156
  if (buttonLike && (e.key === 'Enter' || e.key === ' ')) {
@@ -144,50 +166,130 @@ export const Btn = ({ children, grow, gap, p, click, color, ghost, sm, ...rest }
144
166
  const c = rcn(rest as DProps)
145
167
  return <button className={CN("rounded cursor-pointer", sm ? "px-3 py-1 text-xs" : "px-4 py-2 text-sm",
146
168
  ghost ? "bg-transparent text-zinc-400 hover:bg-zinc-800" : color ? colorCn(color) : "bg-zinc-800 hover:bg-zinc-700", grow && "flex-1", c)}
147
- style={dStyle({ gap, p })} onClick={click} {...rest}>{children}</button>
169
+ style={dStyle({ gap, p, wd: rest.wd, style: rest.style })} onClick={click} {...rest}>{children}</button>
148
170
  }
149
171
 
150
172
  export const Tint = (props: DProps & { color: Color }) => {
151
- const { children, color, grow, gap, p } = props
152
- return <D cn={CN("rounded text-sm", tintCn(color), rcn(props))} grow={grow} gap={gap} p={p ?? 3}>{children}</D>
173
+ const { children, color, grow, gap, p, wd, ht } = props
174
+ return <D cn={CN("rounded text-sm", tintCn(color), rcn(props))} grow={grow} gap={gap} p={p ?? 3} wd={wd} ht={ht}>{children}</D>
153
175
  }
154
176
 
155
177
  export const Row = (props: DProps & { ratio?: number, align?: 'mid' | 'start' | 'end' }) => {
156
- const { children, ratio, align, grow, gap, p } = props
178
+ const { children, ratio, align, grow, gap, p, wd, ht, style } = props
157
179
  return <div className={CN("flex items-center justify-center", align === 'mid' && "justify-between", align == 'end' && "justify-end", align == 'start' && "justify-start", grow && "flex-1", rcn(props))}
158
- style={{ gap: sc(gap ?? 4), ...(p !== undefined && { padding: sc(p) }), ...(ratio ? { width: `${ratio * 100}%` } : {}) }}>{children}</div>
180
+ style={{ ...dStyle({ gap: gap ?? 4, p, wd, ht, style }), ...(ratio ? { width: `${ratio * 100}%` } : {}) }}>{children}</div>
159
181
  }
160
182
 
161
183
  export const Toolbar = (props: DProps) => {
162
- const { children, gap, p } = props
163
- return <Row cn={CN("border-b border-zinc-700", rcn(props))} gap={gap} p={p ?? 2} align="mid">{children}</Row>
184
+ const { children, gap, p, wd, ht } = props
185
+ return <Row cn={CN("border-b border-zinc-700", rcn(props))} gap={gap} p={p ?? 2} wd={wd} ht={ht} align="mid">{children}</Row>
164
186
  }
165
187
 
166
188
  export const Modal = (props: DProps & { open: boolean }) => {
167
- const { children, open, gap, p } = props
189
+ const { children, open, gap, p, wd, ht } = props
168
190
  return open ? <div className="fixed inset-0 flex items-center justify-center bg-black/50 z-50">
169
- <Card cn={CN("max-h-[80vh] overflow-y-auto w-lg", rcn(props))} gap={gap} p={p}>{children}</Card>
191
+ <Card cn={CN("max-h-[80vh] overflow-y-auto w-lg", rcn(props))} gap={gap} p={p} wd={wd} ht={ht}>{children}</Card>
170
192
  </div> : null
171
193
  }
172
194
 
173
195
  export const Input = ({ cn, cnIgnoreWrongUsage, grow, ...rest }: DProps & React.InputHTMLAttributes<HTMLInputElement>) => {
174
196
  const c = rcn({ cn, cnIgnoreWrongUsage })
175
- return <input className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none", grow && "flex-1", c)} {...rest} />
197
+ return <input className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none", grow && "flex-1", c)} style={dStyle({ gap: rest.gap, p: rest.p, wd: rest.wd, style: rest.style })} {...rest} />
176
198
  }
177
199
 
178
- export const Textarea = ({ cn, cnIgnoreWrongUsage, grow, ...rest }: DProps & React.TextareaHTMLAttributes<HTMLTextAreaElement>) => {
200
+ export const Textarea = ({ cn, cnIgnoreWrongUsage, ref, grow, ...rest }: DProps & React.TextareaHTMLAttributes<HTMLTextAreaElement> & { ref?: React.RefObject<HTMLTextAreaElement | null> }) => {
179
201
  const c = rcn({ cn, cnIgnoreWrongUsage })
180
- return <textarea className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none w-full", grow && "flex-1", c)} {...rest} />
202
+ return <textarea ref={ref} className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none w-full", grow && "flex-1", c)} style={dStyle({ gap: rest.gap, p: rest.p, wd: rest.wd, style: rest.style })} {...rest} />
181
203
  }
182
204
 
183
205
  export const Select = ({ cn, cnIgnoreWrongUsage, grow, ...rest }: DProps & React.SelectHTMLAttributes<HTMLSelectElement>) => {
184
206
  const c = rcn({ cn, cnIgnoreWrongUsage })
185
- return <select className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none cursor-pointer", grow && "flex-1", c)} {...rest} />
207
+ return <select className={CN("rounded bg-zinc-800 border border-zinc-700 px-3 py-2 text-sm outline-none cursor-pointer", grow && "flex-1", c)} style={dStyle({ gap: rest.gap, p: rest.p, wd: rest.wd, style: rest.style })} {...rest} />
186
208
  }
187
209
 
188
210
  export const Grid = (props: DProps & { cols?: number }) => {
189
- const { children, cols, grow, gap, p } = props
190
- return <D cn={CN("grid ui-grid", rcn(props))} grow={grow} p={p} style={{ gap: sc(gap ?? 4), '--cols': cols ?? Children.count(children) } as React.CSSProperties}>{children}</D>
211
+ const { children, cols, grow, gap, p, wd, ht } = props
212
+ return <D cn={CN("grid ui-grid", rcn(props))} grow={grow} p={p} wd={wd} ht={ht} style={{ gap: sc(gap ?? 4), '--cols': cols ?? Children.count(children) } as React.CSSProperties}>{children}</D>
213
+ }
214
+
215
+ export const Hr = ({ vertical, color = 'gray', grow, gap, p, ...rest }: DProps & { vertical?: boolean, color?: Color | 'gray' }) => {
216
+ const c = rcn(rest as DProps)
217
+ const border = color === 'gray' ? "border-zinc-700" : ({
218
+ red: "border-red-500",
219
+ blue: "border-blue-500",
220
+ orange: "border-orange-500",
221
+ purple: "border-purple-500",
222
+ yellow: "border-yellow-500",
223
+ green: "border-green-500",
224
+ } satisfies Record<Color, string>)[color]
225
+
226
+ return <div
227
+ className={CN(vertical ? "self-stretch border-l" : "w-full border-t", border, grow && "flex-1", c)}
228
+ style={dStyle({ gap, p, wd: rest.wd, style: rest.style })}
229
+ aria-hidden="true"
230
+ />
231
+ }
232
+
233
+ export const Progress = ({ value, color = 'blue', dot, grow, gap, p, ...rest }: DProps & { value: number, color?: Color, dot?: boolean }) => {
234
+ const c = rcn(rest as DProps)
235
+ const v = Math.max(0, Math.min(1, value))
236
+ const fill = color === 'red' ? 'bg-red-500'
237
+ : color === 'orange' ? 'bg-orange-500'
238
+ : color === 'purple' ? 'bg-purple-500'
239
+ : color === 'yellow' ? 'bg-yellow-500'
240
+ : color === 'green' ? 'bg-green-500'
241
+ : 'bg-blue-500'
242
+
243
+ if (dot) return <div
244
+ className={CN("rounded-full border border-zinc-700 bg-zinc-800 overflow-hidden", grow && "flex-1", c)}
245
+ style={dStyle({ gap, p, wd: rest.wd, style: { aspectRatio: '1 / 1', ...rest.style } })}
246
+ >
247
+ <div className={CN("h-full rounded-full", fill)} style={{ opacity: v ? 1 : 0.2 }} />
248
+ </div>
249
+
250
+ return <div
251
+ className={CN("h-2 overflow-hidden rounded-full bg-zinc-800 border border-zinc-700", grow && "flex-1", c)}
252
+ style={dStyle({ gap, p, wd: rest.wd, style: rest.style })}
253
+ >
254
+ <div className={CN("h-full rounded-full", fill)} style={{ width: `${v * 100}%` }} />
255
+ </div>
256
+ }
257
+
258
+ export const Dropzone = ({ children, onFiles, multiple, accept, click, ...rest }: DProps & { onFiles: (files: File[]) => void, multiple?: boolean, accept?: string, click?: () => void }) => {
259
+ const input = useRef<HTMLInputElement>(null)
260
+ const c = rcn(rest)
261
+ const push = (list: FileList | null) => {
262
+ const files = list ? Array.from(list) : []
263
+ if (files.length) onFiles(files)
264
+ }
265
+
266
+ return <div
267
+ className={CN("contents", c)}
268
+ onClick={e => {
269
+ click?.()
270
+ input.current?.click()
271
+ rest.onClick?.(e as any)
272
+ }}
273
+ onDragOver={e => {
274
+ e.preventDefault()
275
+ rest.onDragOver?.(e)
276
+ }}
277
+ onDrop={e => {
278
+ e.preventDefault()
279
+ push(e.dataTransfer.files)
280
+ rest.onDrop?.(e)
281
+ }}
282
+ >
283
+ <input
284
+ ref={input}
285
+ hidden
286
+ type="file"
287
+ multiple={multiple}
288
+ accept={accept}
289
+ onChange={e => push(e.target.files)}
290
+ />
291
+ {children}
292
+ </div>
191
293
  }
192
294
 
193
295
  export const Code = (props: DProps & { highlight?: string }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "b44ui",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "index.tsx",
package/readme.md CHANGED
@@ -28,14 +28,19 @@ export default defineConfig({
28
28
  |-----------|-------|-------------|
29
29
  | `App` | `center`, `width`, `cn` | Root layout, dark background, wraps strings in `Md` |
30
30
  | `Centered` | `width`, `cn`, `grow` | Centered max-width container |
31
- | `D` | `cn`, `style`, `grow` | Plain div with `cn` |
32
- | `Row` | `align`, `ratio`, `cn`, `grow` | Flex row, `align`: `start \| mid \| end` |
33
- | `Col` | `cn`, `grow` | Flex column |
31
+ | `D` | `cn`, `style`, `grow`, `wd` | Plain div with `cn` |
32
+ | `Row` | `align`, `ratio`, `cn`, `grow`, `wd` | Flex row, `align`: `start \| mid \| end` |
33
+ | `Col` | `cn`, `grow`, `wd` | Flex column |
34
34
  | `Code` | `highlight` | Code block |
35
35
  | `Grid` | `cols`, `cn`, `grow` | CSS grid, defaults to one column per child |
36
36
  | `Card` | `cn`, `grow` | Bordered zinc-900 card |
37
37
  | `Block` | `label`, `row`, `dashed`, `cn`, `grow` | Padded container with optional label |
38
38
  | `BlockSm` | `dashed`, `cn`, `grow` | Smaller padded container |
39
+ | `TabList` | `gap`, `p`, `wd` | Simple tab row wrapper |
40
+ | `Tab` | `title`, `active`, `click`, `wd` | String-first tab primitive |
41
+ | `Hr` | `vertical`, `color`, `wd` | Horizontal or vertical divider |
42
+ | `Progress` | `value`, `color`, `dot`, `wd` | Progress bar or dot |
43
+ | `Dropzone` | `onFiles`, `multiple`, `accept` | Hidden file input wrapper for click/drop |
39
44
  | `Btn` | `click`, `color`, `ghost`, `cn`, `grow` | Button, supports `Color` and ghost style |
40
45
  | `A` | `href`, `click`, `cn`, `grow` | Link-styled anchor, works with `onClick` or `href` |
41
46
  | `Chip` | `click`, `cn` | Small inline badge, clickable if `click` provided |
@@ -48,6 +53,6 @@ export default defineConfig({
48
53
  | `Popover` | `text`, `color`, `cn` | Hover popover |
49
54
  | `Md` | `className` | Markdown renderer with syntax highlighting |
50
55
 
51
- `Color`: `red \| blue \| orange \| purple \| yellow`
56
+ `Color`: `red \| blue \| orange \| purple \| yellow \| green`
52
57
 
53
58
  All components accept `cn` for additional Tailwind classes (merged via `tailwind-merge`).
package/styles.css CHANGED
@@ -1,22 +1,21 @@
1
1
  @import "highlight.js/styles/github-dark.css";
2
2
 
3
3
  .ui-markdown { line-height: 1.5; }
4
- .ui-markdown h1, .ui-markdown h2, .ui-markdown h3 { font-weight: 700; margin: 0; }
5
- .ui-markdown h1 { font-size: 1.5em; }
6
- .ui-markdown h2 { font-size: 1.25em; }
7
- .ui-markdown h3 { font-size: 1.1em; }
8
- .ui-markdown p { margin: 0.4em 0; }
4
+ h1, .ui-markdown h2, .ui-markdown h3 { font-weight: 700; margin: 0; }
5
+ h1 { font-size: 1.5em; }
6
+ h2 { font-size: 1.25em; }
7
+ h3 { font-size: 1.1em; }
8
+ p { margin: 0.4em 0; }
9
+ a { color: #93c5fd; text-decoration: underline; }
10
+ pre { background: #1a1a2e; border-radius: 4px; padding: 0; overflow-x: auto; margin: 0.4em 0; }
11
+ code { font-family: ui-monospace, monospace; font-size: 0.9em; }
12
+ blockquote { border-left: 3px solid #444; padding-left: 1em; opacity: 0.8; margin: 0.25em 0; }
13
+ img { max-width: 100%; border-radius: 6px; }
9
14
  .ui-markdown p:first-child { margin-top: 0; }
10
15
  .ui-markdown p:last-child { margin-bottom: 0; }
11
- .ui-markdown pre { background: #1a1a2e; border-radius: 4px; padding: 0; overflow-x: auto; margin: 0.4em 0; }
12
- .ui-markdown code { font-family: ui-monospace, monospace; font-size: 0.9em; }
13
16
  .ui-markdown :not(pre) > code { background: #1a1a2e; padding: 2px 5px; border-radius: 3px; }
14
17
  .ui-markdown ul, .ui-markdown ol { padding-left: 1.5em; margin: 0.25em 0; }
15
- .ui-markdown blockquote { border-left: 3px solid #444; padding-left: 1em; opacity: 0.8; margin: 0.25em 0; }
16
- .ui-markdown a { color: #93c5fd; text-decoration: underline; }
17
18
  .ui-markdown table { border-collapse: collapse; margin: 0.4em 0; }
18
19
  .ui-markdown th, .ui-markdown td { border: 1px solid #333; padding: 4px 10px; }
19
- .ui-markdown img { max-width: 100%; border-radius: 6px; }
20
-
21
20
  .ui-grid { grid-template-columns: repeat(var(--cols), 1fr); }
22
21
  @media (max-width: 640px) { .ui-grid { grid-template-columns: 1fr; } }