﻿---
title: "Lab: Custom Components Preview"
date: 2023-09-02
excerpt: Preview the custom Markdown components used by this blog theme, with usage notes and source examples.
tags:
  - Blog
  - CSS
  - Markdown
updated: 2026-05-23 01:18:48
i18n:
  cn: /test_components
---

<script data-swup-reload-script type="module" src="/js/components/tab.js"></script>

This page previews the custom components available in this blog theme. Each section shows the rendered component first, then the Markdown or HTML needed to reproduce it.

These components are custom HTML elements embedded in Markdown. Always load the matching module before using a component, and include `data-swup-reload-script` on the script tag so the component is reloaded after client-side navigation.

Use this page as a quick reference, but keep the components purposeful in real posts. A component should make structure, comparison, or context clearer. If plain Markdown communicates the idea better, use plain Markdown.

## Tabs

Tabs are useful when the reader needs to compare related views without scrolling through repeated sections. In this page, each component uses tabs to separate the live preview from the source Markdown.

Keep tab labels short. Add `active` to the default `<x-tab>`, and use one `<x-tab title="...">` per panel. Leave blank lines around tab content, especially when the content contains Markdown blocks or custom HTML.

## Image Carousel

Use an image carousel when several images belong to one sequence, scene, or comparison. It works well for travel notes, visual walkthroughs, and compact galleries.

Load `/js/components/image-carousel.js` before the component. Put Markdown image syntax inside `<image-carousel>`, with blank lines around the image list. Use `autoplay` only when motion helps the page; otherwise let the reader move through the images manually. The `interval` value is in milliseconds.

The aspect ratio is derived from the first image's natural dimensions, so a mixed set of portraits and landscapes won't get cropped to a fixed 3/2 box. Pass an explicit `ratio` (e.g. `ratio="16/9"`) to override when you want every slide pinned to the same shape. Navigation is exposed through prev/next buttons, dots, keyboard arrows, and touch swipe; autoplay automatically pauses on hover, when the carousel scrolls out of view, or when the tab is hidden.

<x-tabs>

<x-tab title="Preview" active>

<script data-swup-reload-script type="module" src="/js/components/image-carousel.js"></script>
<image-carousel autoplay interval="4000">

![登山](https://assets.vluv.space/登山者.avif)

![渔村](https://assets.vluv.space/珠山秀谷-珠山海边村落.avif)

![山,海,船](https://assets.vluv.space/山海.avif)

![映山湖与海](https://assets.vluv.space/映山湖与海.avif)

</image-carousel>

</x-tab>

<x-tab title="Markdown">

```md
<script data-swup-reload-script type="module" src="/js/components/image-carousel.js"></script>
<image-carousel autoplay interval="4000">

![登山](https://assets.vluv.space/登山者.avif)
![渔村](https://assets.vluv.space/珠山秀谷-珠山海边村落.avif)
![山,海,船](https://assets.vluv.space/山海.avif)
![映山湖与海](https://assets.vluv.space/映山湖与海.avif)

</image-carousel>
```

</x-tab>

</x-tabs>

## Text Image Section

Use a text image section when an image and a paragraph need to be read together. It is better than a standalone image when the picture provides context for a person, place, quote, or historical note.

Load `/js/components/text-image-section.js` before the component. Provide a meaningful `alt` value, set `width` deliberately, and keep the text focused. Long text can work, but the component reads best when the image and copy form one compact unit.

The image renders as a `<figure>` (with the `alt` text reused as a `<figcaption>`) floated to the right of the text by default; add the `left` attribute to flip the layout. On screens narrower than 640px the figure stacks above the text full-width. Internally the host element creates its own block formatting context, so a parent `<blockquote>` that Hexo wraps around `>`-prefixed Markdown lines won't bleed its left border into the image area on mobile.

<x-tabs>

<x-tab title="Preview" active>

<script data-swup-reload-script type="module" src="/js/components/text-image-section.js"></script>
<text-image-section image="https://www.myinterestingfacts.com/wp-content/uploads/2014/03/Hermann-Hesse-Image.jpg" alt="Hermann Hesse" width="200px" font-size="1.1rem" font-family="homemade-apple">

**Hermann Karl Hesse** (2 July 1877 – 9 August 1962) was a German-Swiss poet and novelist, and winner of the 1946 Nobel Prize in Literature. His interest in Eastern religious, spiritual, and philosophical traditions, combined with his involvement with Jungian analysis, helped to shape his literary work. His best-known novels include Demian, Steppenwolf, Siddhartha, Narcissus and Goldmund, and The Glass Bead Game, each of which explores an individual's search for authenticity, self-knowledge, and spirituality.

</text-image-section>

</x-tab>

<x-tab title="Markdown">

```md
<script data-swup-reload-script type="module" src="/js/components/text-image-section.js"></script>
<text-image-section image="https://www.myinterestingfacts.com/wp-content/uploads/2014/03/Hermann-Hesse-Image.jpg" alt="Hermann Hesse" width="200px" font-size="1.1rem" font-family="homemade-apple">

**Hermann Karl Hesse** (2 July 1877 – 9 August 1962) was a German-Swiss poet and novelist, and winner of the 1946 Nobel Prize in Literature. His interest in Eastern religious, spiritual, and philosophical traditions, combined with his involvement with Jungian analysis, helped to shape his literary work. His best-known novels include Demian, Steppenwolf, Siddhartha, Narcissus and Goldmund, and The Glass Bead Game, each of which explores an individual's search for authenticity, self-knowledge, and spirituality.

</text-image-section>
```

  </x-tab>

</x-tabs>

## Accordion 🪗

Use an accordion for long optional explanations. It keeps the main page readable while still allowing interested readers to expand details.

Load `/js/components/accordion.js` before the component. Each `<accordion-item>` needs a clear `title`. Keep blank lines between the item tags and the Markdown content. Indent nested Markdown consistently, especially lists and code blocks, because malformed spacing can break rendering inside custom elements.

<x-tabs>

<x-tab title="Preview" active>

<script data-swup-reload-script type="module" src="/js/components/accordion.js"></script>
<x-accordion>
  <accordion-item title="Headings">

  Use `#` to create headings. The count of `#` determines the level. Markdown supports six heading levels, corresponding to `<h1>` through `<h6>`. Typically a post has one top-level heading as the page title, and body content starts from level two.

  ```md
  # Heading 1
  ## Heading 2
  ### Heading 3
  ```

  </accordion-item>
  <accordion-item title="Text Styles">

  Use `**bold**` and `*italic*` for emphasis. Strikethrough uses `~~text~~`. Inline code wraps in backticks, e.g. `` `code` ``.

  These styles can be combined freely:

  - **bold** `**text**`
  - *italic* `*text*`
  - ~~strikethrough~~ `~~text~~`
  - `inline code` `` `code` ``

  </accordion-item>
  <accordion-item title="Links &amp; Images">

  Link syntax is `[text](URL)`. Images add a `!` prefix: `![alt text](URL)`.

  ```md
  [OpenAI](https://openai.com)
  ![Logo](https://example.com/logo.png)
  ```

  URLs also support reference-style notation, useful when the same link appears multiple times.

  </accordion-item>
</x-accordion>

</x-tab>

<x-tab title="Markdown">

````md
<script data-swup-reload-script type="module" src="/js/components/accordion.js"></script>
<x-accordion>
  <accordion-item title="Headings">

  Use `#` to create headings. The count of `#` determines the level. Markdown supports six heading levels, corresponding to `<h1>` through `<h6>`. Typically a post has one top-level heading as the page title, and body content starts from level two.

  ```md
  # Heading 1
  ## Heading 2
  ### Heading 3
  ```

  </accordion-item>
  <accordion-item title="Text Styles">

  Use `**bold**` and `*italic*` for emphasis. Strikethrough uses `~~text~~`. Inline code wraps in backticks, e.g. `` `code` ``.

  These styles can be combined freely:

  - **bold** `**text**`
  - *italic* `*text*`
  - ~~strikethrough~~ `~~text~~`
  - `inline code` `` `code` ``

  </accordion-item>
  <accordion-item title="Links &amp; Images">

  Link syntax is `[text](URL)`. Images add a `!` prefix: `![alt text](URL)`.

  ```md
  [OpenAI](https://openai.com)
  ![Logo](https://example.com/logo.png)
  ```

  URLs also support reference-style notation, useful when the same link appears multiple times.

  </accordion-item>
</x-accordion>
````

</x-tab>

</x-tabs>

## File Tree 🌳

Use a file tree when directory structure is part of the explanation. It is especially useful for themes, scripts, generated assets, and small project layouts.

Load `/js/components/tree.js` before the component. Use `root` to name the top-level directory. Inside `<x-tree>`, write each entry as a Markdown list item and use two spaces for each nesting level. Add a trailing `/` when a folder has no listed children, so the component can still render it as a folder. Keep the tree small enough to scan.

<x-tabs>

<x-tab title="Preview" active>

<script data-swup-reload-script type="module" src="/js/components/tree.js"></script>
<x-tree root="gnix-theme">

- source/
  - js/
    - components/
      - accordion.js
      - tree.js
      - image-carousel.js
    - main.js
  - css/
    - style.css
    - themes/
      - nord.css
      - catppuccin.css
  - images/
    - logo.png
- scripts/
- package.json
- README.md
- hexo.config.js

</x-tree>

</x-tab>

<x-tab title="Markdown">

```md
<script data-swup-reload-script type="module" src="/js/components/tree.js"></script>
<x-tree root="gnix-theme">

- source/
  - js/
    - components/
      - accordion.js
      - tree.js
      - image-carousel.js
    - main.js
  - css/
    - style.css
    - themes/
      - nord.css
      - catppuccin.css
  - images/
    - logo.png
- scripts/
- package.json
- README.md
- hexo.config.js

</x-tree>
```

</x-tab>

</x-tabs>

## Side Note

Use a side note when supporting context should stay near the paragraph it explains without interrupting the main reading flow. It is useful for short definitions, source quotes, caveats, or implementation notes.

Load `/js/components/sidenote.js` before the component. Place `<side-note>` immediately after the paragraph or block it annotates. On wide screens the note moves into the right margin and multiple notes are measured so they do not overlap; on smaller screens it falls back into the normal article flow.


<script data-swup-reload-script type="module" src="/js/components/sidenote.js"></script>

This paragraph introduces the main claim. The side note below adds context without forcing the reader to leave the sentence they are reading.

<side-note>Side notes sit in the article margin on wide screens. They are best kept short and focused.</side-note>

This second paragraph has another side note, which demonstrates that stacked notes avoid each other instead of sharing the same top position.

<side-note>If two notes would collide, the later one is pushed down while staying close to its anchor paragraph.</side-note>

```md
<script data-swup-reload-script type="module" src="/js/components/sidenote.js"></script>

This paragraph introduces the main claim. The side note below adds context without forcing the reader to leave the sentence they are reading.

<side-note>Side notes sit in the article margin on wide screens. They are best kept short and focused.</side-note>

This second paragraph has another side note, which demonstrates that stacked notes avoid each other instead of sharing the same top position.

<side-note>If two notes would collide, the later one is pushed down while staying close to its anchor paragraph.</side-note>
```


## Chat 💬

Use the chat component when a question-and-answer format makes the explanation easier to follow. It works well for debugging notes, AI-assisted explanations, and short conversations where the sequence matters.

Load `/js/components/chat.js` before the component. Each `<chat-message>` should include a `name`; `avatar` and `timestamp` are optional but help the conversation feel structured. Add `is-me` to align the author's own message. Keep each message concise, or the chat layout will become harder to scan than normal prose.

The layout uses a two-row grid: avatar and header (name + timestamp) sit on the first row, with the message bubble on the second. On mobile the bubble drops to a full-width row below the header, so the avatar no longer eats horizontal space. Inline `<code>`, `<kbd>`, lists, and code blocks inside the bubble pick up dedicated styles that don't leak out of the shadow DOM.

<x-tabs>

<x-tab title="Preview" active>

<script data-swup-reload-script type="module" src="/js/components/chat.js"></script>
<x-chat>
  <chat-message name="Alice" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Alice" timestamp="10:30">
    Hey everyone! How's the project going?
  </chat-message>
  <chat-message name="Bob" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Bob" timestamp="10:32">
    Going well! Just finished the authentication module.
  </chat-message>
  <chat-message name="Charlie" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Charlie" timestamp="10:35">
    Nice! We're using JWT tokens with refresh token rotation. Here's the basic structure:
    <code>auth.refreshToken.rotate()</code>
  </chat-message>
  <chat-message name="Alice" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Alice" timestamp="10:38" is-me>
    Awesome work! Can't wait to see the PR
  </chat-message>
</x-chat>

</x-tab>

<x-tab title="Markdown">

```md
<script data-swup-reload-script type="module" src="/js/components/chat.js"></script>
<x-chat>
  <chat-message name="Alice" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Alice" timestamp="10:30">
    Hey everyone! How's the project going?
  </chat-message>
  <chat-message name="Bob" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Bob" timestamp="10:32">
    Going well! Just finished the authentication module.
  </chat-message>
  <chat-message name="Charlie" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Charlie" timestamp="10:35">
    Nice! We're using JWT tokens with refresh token rotation. Here's the basic structure:
    <code>auth.refreshToken.rotate()</code>
  </chat-message>
  <chat-message name="GnixAij" avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=GnixAij" timestamp="10:38" is-me>
    Awesome work! Can't wait to see the PR
  </chat-message>
</x-chat>
```

</x-tab>

</x-tabs>

## Read More

- [[test_flavored_md| Flavored Markdown]]
- [[math_preview| Math]]
