@k11k/better-blocks-astro-renderer 0.4.1 → 0.5.0

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 CHANGED
@@ -147,6 +147,31 @@ const { blocks } = Astro.props;
147
147
  <BlocksRenderer content={blocks} blocks={{ callout: MyCallout }} />
148
148
  ```
149
149
 
150
+ ### Details / Summary (Collapsible)
151
+
152
+ Block-level `details` nodes render a native, keyboard-accessible `<details>` / `<summary>` disclosure with **zero client-side JavaScript** &mdash; the open/closed state is handled entirely by the browser. The `summary` field is the plain-text label, the optional `defaultOpen` boolean maps to the HTML `open` attribute (honored on initial render so screen readers get the correct state), and `children` are block-level content (paragraphs, lists, tables, images, and nested `details`) rendered after the summary. The default markup carries stable `bb-details` and `bb-details-summary` classes.
153
+
154
+ A small **scoped `<style>`** ships with the component (still zero client-side JavaScript): a GitHub-inspired card with a rotating disclosure marker. Retheme it from your own CSS via the `--bb-details-*` custom properties (`--bb-details-border`, `--bb-details-bg`, `--bb-details-summary-bg`, `--bb-details-marker`) without replacing the markup:
155
+
156
+ ```css
157
+ .bb-details {
158
+ --bb-details-border: #c8c8c8;
159
+ --bb-details-summary-bg: #eee;
160
+ }
161
+ ```
162
+
163
+ To replace the markup entirely, override the `details` block. It receives `summary` and `defaultOpen`; the nested children arrive via `<slot />`:
164
+
165
+ ```astro
166
+ ---
167
+ import { BlocksRenderer } from '@k11k/better-blocks-astro-renderer';
168
+ import MyDetails from '../components/MyDetails.astro';
169
+ const { blocks } = Astro.props;
170
+ ---
171
+
172
+ <BlocksRenderer content={blocks} blocks={{ details: MyDetails }} />
173
+ ```
174
+
150
175
  ## Supported Blocks
151
176
 
152
177
  | Block | Default element | Source |
@@ -165,6 +190,7 @@ const { blocks } = Astro.props;
165
190
  | `math` (inline/block) | `<span>` / `<div>` | Better Blocks |
166
191
  | `diagram` (mermaid) | `<div>` (inline SVG) | Better Blocks |
167
192
  | `callout` (admonition) | `<aside>` | Better Blocks |
193
+ | `details` (collapsible) | `<details>` | Better Blocks |
168
194
 
169
195
  ### Block properties
170
196
 
@@ -186,6 +212,8 @@ const { blocks } = Astro.props;
186
212
  | `value` | math | LaTeX source rendered with KaTeX |
187
213
  | `format` | diagram | `mermaid` (the only supported diagram format) |
188
214
  | `value` | diagram | Mermaid source, pre-rendered to SVG on the server |
215
+ | `summary` | details | Plain-text label for the `<summary>` |
216
+ | `defaultOpen` | details | Open on initial render (HTML `open` attribute) |
189
217
 
190
218
  ## Supported Modifiers
191
219
 
package/index.ts CHANGED
@@ -24,6 +24,7 @@ export type {
24
24
  DiagramNode,
25
25
  CalloutNode,
26
26
  CalloutVariant,
27
+ DetailsNode,
27
28
  TextAlign,
28
29
  StyleValue,
29
30
  CustomBlocksConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k11k/better-blocks-astro-renderer",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Astro renderer for Strapi v5 Blocks content with full Better Blocks plugin support — colors, tables, to-do lists, media embeds, alignment, and more. Native Astro components, zero client-side JavaScript.",
5
5
  "type": "module",
6
6
  "types": "./index.ts",
package/src/Block.astro CHANGED
@@ -6,6 +6,7 @@ import Table from './Table.astro';
6
6
  import Math from './Math.astro';
7
7
  import Diagram from './Diagram.astro';
8
8
  import Callout from './Callout.astro';
9
+ import Details from './Details.astro';
9
10
  import { getBlockStyle, getPlainText } from './utils';
10
11
 
11
12
  interface Props {
@@ -160,3 +161,5 @@ const EmbedComp = blocks?.['media-embed'] as any;
160
161
  {block.type === 'diagram' && <Diagram node={block} blocks={blocks} />}
161
162
 
162
163
  {block.type === 'callout' && <Callout node={block} blocks={blocks} modifiers={modifiers} />}
164
+
165
+ {block.type === 'details' && <Details node={block} blocks={blocks} modifiers={modifiers} />}
@@ -0,0 +1,84 @@
1
+ ---
2
+ import type { DetailsNode, CustomBlocksConfig, CustomModifiersConfig } from './types';
3
+ import Block from './Block.astro';
4
+
5
+ interface Props {
6
+ node: DetailsNode;
7
+ blocks?: CustomBlocksConfig;
8
+ modifiers?: CustomModifiersConfig;
9
+ }
10
+
11
+ const { node, blocks, modifiers } = Astro.props;
12
+
13
+ const DetailsComp = blocks?.details as any;
14
+ ---
15
+
16
+ {
17
+ DetailsComp ? (
18
+ <DetailsComp summary={node.summary} defaultOpen={node.defaultOpen}>
19
+ {node.children.map((child) => (
20
+ <Block block={child} blocks={blocks} modifiers={modifiers} />
21
+ ))}
22
+ </DetailsComp>
23
+ ) : (
24
+ <details class="bb-details" open={node.defaultOpen}>
25
+ <summary class="bb-details-summary">{node.summary}</summary>
26
+ <div class="bb-details-body">
27
+ {node.children.map((child) => (
28
+ <Block block={child} blocks={blocks} modifiers={modifiers} />
29
+ ))}
30
+ </div>
31
+ </details>
32
+ )
33
+ }
34
+
35
+ <style>
36
+ /* GitHub-inspired collapsible. Zero client-side JavaScript — the open/closed
37
+ state is handled natively by the <details>/<summary> elements. */
38
+ .bb-details {
39
+ border: 1px solid var(--bb-details-border, #d0d7de);
40
+ border-radius: 6px;
41
+ margin: 1rem 0;
42
+ background: var(--bb-details-bg, #fff);
43
+ }
44
+ .bb-details-summary {
45
+ cursor: pointer;
46
+ padding: 0.5rem 1rem;
47
+ font-weight: 600;
48
+ list-style: none;
49
+ border-radius: 6px;
50
+ background: var(--bb-details-summary-bg, #f6f8fa);
51
+ }
52
+ /* Hide the browser's native disclosure triangle and draw our own that
53
+ rotates when the disclosure is open. */
54
+ .bb-details-summary::-webkit-details-marker {
55
+ display: none;
56
+ }
57
+ .bb-details-summary::before {
58
+ content: '\25B8';
59
+ display: inline-block;
60
+ margin-right: 0.5rem;
61
+ color: var(--bb-details-marker, #57606a);
62
+ transition: transform 0.15s ease;
63
+ }
64
+ .bb-details[open] > .bb-details-summary::before {
65
+ transform: rotate(90deg);
66
+ }
67
+ .bb-details[open] > .bb-details-summary {
68
+ border-bottom: 1px solid var(--bb-details-border, #d0d7de);
69
+ border-radius: 6px 6px 0 0;
70
+ }
71
+ /* Inset the body content. Collapse the body's outer margins so it sits flush
72
+ within the box (top/bottom balanced). :global is required because the body
73
+ blocks are rendered by other components and don't carry this component's
74
+ scoped id. */
75
+ .bb-details-body {
76
+ padding: 0.5rem 1rem;
77
+ }
78
+ .bb-details-body > :global(:first-child) {
79
+ margin-top: 0;
80
+ }
81
+ .bb-details-body > :global(:last-child) {
82
+ margin-bottom: 0;
83
+ }
84
+ </style>
package/src/types.ts CHANGED
@@ -144,6 +144,13 @@ export type CalloutNode = {
144
144
  children: BlockNode[];
145
145
  };
146
146
 
147
+ export type DetailsNode = {
148
+ type: 'details';
149
+ summary: string;
150
+ defaultOpen?: boolean;
151
+ children: BlockNode[];
152
+ };
153
+
147
154
  export type BlockNode =
148
155
  | ParagraphNode
149
156
  | HeadingNode
@@ -156,7 +163,8 @@ export type BlockNode =
156
163
  | MediaEmbedNode
157
164
  | MathNode
158
165
  | DiagramNode
159
- | CalloutNode;
166
+ | CalloutNode
167
+ | DetailsNode;
160
168
 
161
169
  export type BlocksContent = BlockNode[];
162
170
 
@@ -196,6 +204,7 @@ export type AstroComponentFactory = (...args: any[]) => any;
196
204
  * - `math` — `{ formula: string; inline: boolean }`
197
205
  * - `diagram` — `{ code: string; format: 'mermaid' }`
198
206
  * - `callout` — `{ variant: CalloutVariant; title?: string }` (children via `<slot />`)
207
+ * - `details` — `{ summary: string; defaultOpen?: boolean }` (children via `<slot />`)
199
208
  */
200
209
  export type CustomBlocksConfig = Partial<{
201
210
  paragraph: AstroComponentFactory;
@@ -215,6 +224,7 @@ export type CustomBlocksConfig = Partial<{
215
224
  math: AstroComponentFactory;
216
225
  diagram: AstroComponentFactory;
217
226
  callout: AstroComponentFactory;
227
+ details: AstroComponentFactory;
218
228
  }>;
219
229
 
220
230
  /**