Skip to content
Back to skills

fancy-kanban

ASecurity

Generate valid fancy-kanban boards — markdown-embedded kanban boards with typed fields, swimlanes, workflow rules, and card face configuration for Obsidian and compatible tools

  • 14 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
toolsgoapifrontendbackend

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 12 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add robertoallende/fancy-kanban --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of fancy-kanban?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for fancy-kanban
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/robertoallende-fancy-kanban/badge)](https://www.skillsdirectory.com/skills/robertoallende-fancy-kanban)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fancy-kanban
description: Generate valid fancy-kanban boards — markdown-embedded kanban boards with typed fields, swimlanes, workflow rules, and card face configuration for Obsidian and compatible tools
---

# Fancy Kanban Board Schema

A self-contained specification for the markdown-based kanban board format. Any app that reads and writes files following this schema will produce compatible boards.

## Overview

Each board lives inside a fenced code block with the identifier `fancy-kanban`. This means:

- A board can be **embedded anywhere in a note**, alongside prose, links, and other content
- A note may contain **zero, one, or multiple** boards
- A file consisting of a single board block is a **standalone board file**
- Without the plugin installed, the block renders as an unstyled code block — the raw text remains fully human-readable

The block contains two sections separated by `---`:

1. A **config section** (YAML-like) defining fields, workflow, and view options
2. A **markdown table** containing the data rows

## Complete Example

````markdown
```fancy-kanban
---
title: My Board
fields:
  - name: title,       type: Text,     label: Title
  - name: status,      type: Select,   options: inbox|doing|done, label: Status, default: inbox
  - name: responsible, type: Text,     label: Responsible
  - name: start_date,  type: Date,     label: Start Date
  - name: notes,       type: Textarea, label: Notes
  - name: effort,      type: Number,   label: Effort
  - name: docs,        type: Link,     label: Docs
  - name: team,        type: Select,   options: frontend|backend, label: Team
card_fields: responsible, effort
lanes: team
workflow: inbox→doing, inbox→done, doing→done, doing→inbox, done→doing, done→inbox
---

| _id    | Title          | Status | Responsible | Start Date | Notes                      | Effort | Docs                  | Team     |
|--------|----------------|--------|-------------|------------|----------------------------|--------|-----------------------|----------|
| x7k2a1 | Fix login bug  | inbox  | Alice       | 2026-01-15 | Needs investigation        | 3      |                       | backend  |
| m3p9b2 | Refactor auth  | doing  | Bob         |            | Multi-line\<br\>content here | 5      | design.md             | backend  |
| q1r4c3 | Setup CI       | done   |             | 2026-01-01 |                            | 1      | setup.md\|guide.md    | frontend |
```
````

## Block Structure

The fenced block contains exactly two sections divided by a `---` line:

```
```fancy-kanban
---
{config}
---

{markdown table}
```
```

Everything before the first `---` is ignored (reserved for future use). Everything between the two `---` lines is the config. Everything after the second `---` is the table.

## Config Section

YAML-like key-value pairs. Parsed line-by-line.

### Keys

| Key | Required | Default | Description |
|-----|----------|---------|-------------|
| `title` | yes | — | Human-readable board name |
| `fields` | yes | — | List of field definitions (see below) |
| `version` | no | `1` | Format version written by the plugin; boards with a version higher than the current plugin supports open in read-only mode |
| `workflow` | no | all transitions allowed | Comma-separated `from→to` pairs |
| `lanes` | no | none | Field name to use as the swimlane grouping dimension |
| `card_title` | no | auto-detect | Field name to use as the card heading; set to empty string to show no heading |
| `card_fields` | no | none | Ordered comma-separated list of secondary fields to display below the card heading |
| `card_labels` | no | `true` | Set to `false` to hide the label prefix on secondary card fields |
| `card_limit` | no | `0` (no limit) | Maximum number of cards shown per column; cards beyond this count are hidden behind a "Show more" button |

### `card_title`

```yaml
card_title: summary
```

Controls which field is rendered as the card heading:

- **Absent** (default): the first non-`_id`, non-column field is used automatically
- **Set to a field name**: that field's value appears as the heading
- **Set to empty string** (`card_title: `): no heading is shown; the card displays only secondary fields

### `card_fields`

```yaml
card_fields: priority, due, docs
```

An ordered, comma-separated list of **secondary** field names to display below the card heading. These appear as labelled rows beneath the title.

**Default behaviour**: when `card_fields` is absent the card shows only the heading field. Existing boards with no `card_fields` key are unaffected.

**Unknown names**: field names that do not match any defined field are silently ignored at render time. Removing a field from `fields:` automatically hides it from the card face without a parse error.

**Link fields**: when a `Link` field is included in `card_fields`, its items are rendered as a horizontal list of clickable links on the card face. Vault paths open in a new tab; external URIs open in the browser.

### `card_labels`

```yaml
card_labels: false
```

When set to `false`, the label prefix is omitted from secondary field rows on the card face — only the value is shown. Defaults to `true` (labels visible). The key is only written to the config when its value is `false`.

### `card_limit`

```yaml
card_limit: 5
```

When set to a positive integer, each column shows at most that many cards. Cards beyond the limit are hidden and a **Show more (N)** button appears at the bottom of the column. Clicking it reveals all remaining cards without reloading the board. Setting `card_limit: 0` or omitting the key disables limiting entirely.

This is particularly useful for boards where the **done** column accumulates many completed cards over time — the most recently added cards remain visible at a glance, while older history collapses out of the way.

### `lanes`

When set to a field name (must be a `Select` field), the board renders as a matrix:

- **Columns** — driven by the `status` field's `options` order
- **Swimlane rows** — driven by the `lanes` field's `options` order

Cards with no value for the lanes field appear in an **Unassigned** pseudo-lane at the bottom. Changing `lanes` is a view-config change only — no row data is affected.

## Field Definitions

Each entry in the `fields` list is a comma-separated set of `key: value` pairs on a single line, prefixed with `- `:

```yaml
fields:
  - name: title, type: Text, label: Title
  - name: status, type: Select, options: inbox|doing|done, label: Status, default: inbox
```

**Field ordering convention**: list `title` first, then `status`, then additional fields. The table column order in the data section mirrors the field definition order, and the auto-detected card heading resolves to the first non-`_id`, non-column field — so placing `title` first ensures predictable heading resolution without needing an explicit `card_title` key.

### Field Properties

| Property | Required | Description |
|----------|----------|-------------|
| `name` | yes | Internal identifier. Lowercase, underscores for spaces. |
| `type` | yes | One of: `Text`, `Textarea`, `Date`, `Number`, `Select`, `Link` |
| `label` | yes | Human-readable column header. Must match the markdown table header exactly. |
| `options` | Select only | Pipe-separated (`\|`) list of allowed values |
| `colors` | Select, optional | Pipe-separated `name=hex` pairs assigning a background color to each option (e.g. `High=#e74c3c\|Low=#27ae60`). Options without an entry render as plain text. |
| `default` | no | Default value for new cards |

### Field Types

| Type | Storage Format | Description |
|------|----------------|-------------|
| `Text` | Plain string | Single-line text |
| `Textarea` | String with `<br>` for newlines | Multi-line text |
| `Date` | `YYYY-MM-DD` | Calendar date |
| `Number` | Integer or decimal string | Numeric value |
| `Select` | One of the `options` values | Constrained choice; options may carry hex colors via the `colors` property |
| `Link` | `<br>`-separated list of paths or URIs | Vault-root-relative paths (`notes/doc.pdf`) or external URIs (`https://…`, `ftp://…`, `mailto:…`) |

### Special Field Names

| Name | Role |
|------|------|
| `title` | **Primary display field.** Should be listed first in `fields:`. Used as the auto-detected card heading when `card_title` is not set. |
| `status` | **Required. Kanban column field.** Must be `type: Select`. Cards are grouped into columns by this value. Column order follows `options` order. |

All other fields are secondary — displayed as card metadata when listed in `card_fields`.

## Item Identity

Every card has a **stable hidden ID** stored in a `_id` column in the markdown table. The UI never displays this column.

- Generated on card creation: short random alphanumeric string (e.g. 8 characters: `x7k2a1b3`)
- Preserved through all serialization round-trips
- Survives reordering, drag-and-drop, and field renames
- Used internally for all card operations (move, edit, delete)

Because identity is ID-based and not derived from content, renaming a card's title has no effect on its identity.

## Workflow

Defines which status transitions are allowed in the UI:

```yaml
workflow: inbox→doing, inbox→done, doing→done, doing→inbox, done→doing, done→inbox
```

- Comma-separated list of `from→to` pairs (arrow: `→`, U+2192)
- If `workflow` is omitted, all transitions between status options are permitted
- Used to constrain UI choices (e.g. a "Move to" menu only shows valid targets)
- Hand-edits to the raw table bypass workflow validation by nature of being plain text

## Board Templates

When creating a new board, a set of predefined templates provides common `status` option sets. Templates only pre-fill the `status` field options and `workflow` — all other fields are added by the user.

| Template | Status options | Default workflow |
|----------|---------------|------------------|
| **Basic** | `inbox \| doing \| done` | All transitions |
| **Software** | `backlog \| todo \| in-progress \| review \| done` | Forward + one step back |
| **Content** | `idea \| draft \| review \| published` | Forward only |
| **Project** | `planning \| active \| blocked \| complete` | All except → planning |
| **Custom** | User-defined | User-defined |

Selecting **Custom** opens a free-form editor for `status` options and workflow transitions.

## Markdown Table

The table appears immediately after the closing `---` of the config section.

### Structure

```
| _id    | Header1 | Header2 |
|--------|---------|---------|
| abc123 | value   | value   |
```

1. **Header row** — first column is always `_id`; remaining columns are field labels in schema definition order
2. **Separator row** — standard markdown table separator
3. **Data rows** — one row per card; `_id` cell contains the card's stable ID

### Rules

- Column order in the table must match field definition order (after `_id`)
- Empty cells are valid (field value = empty string)
- Row order within a status group is the display order
- An empty table (header + separator, no data rows) is valid
- The `_id` column is always first and is never shown in the rendered board

### Escaping

| Character in value | Escaped as |
|-------------------|------------|
| `\|` (pipe) | `\|` (backslash-pipe) |
| Newline | `<br>` |
| Carriage return | `<br>` |

When reading: `\|` → `|`, `<br>` → newline.
When writing: `|` → `\|`, any newline → `<br>`.

## Parsing Algorithm

1. Extract the raw string between the opening and closing fences of the `fancy-kanban` block
2. Split on the first `---` line to locate the config section start
3. Split on the second `---` line to separate config from table
4. Parse config line-by-line: extract `title`, `version`, `fields`, `workflow`, `lanes`, `card_title`, `card_fields`, `card_labels`, `card_limit`
5. For `fields`, collect lines starting with `- ` and parse each as comma-separated `key: value` pairs; `colors` is decoded as pipe-separated `name=hex` tokens
6. Find table lines in the body (lines starting with `|`)
7. First table line is the header row — extract column labels; `_id` is always first
8. Map remaining header labels to field names via case-insensitive label lookup
9. Parse each data row: split on unescaped `|`, trim cells, unescape `\|` and `<br>`
10. First cell of each data row is the card's `_id`
11. Group cards by `status` field value, seeding groups in `options` order

**Schema reconciliation (applied after parsing):**
- Fields present in schema but missing from a card's data: backfill with the field's `default` (or empty string)
- Fields present in card data but removed from the schema: preserve as orphaned hidden data (not deleted)

## Writing Algorithm

1. Reconstruct the config section from the current schema (preserve original formatting where possible)
2. Generate header row: `_id` first, then field labels in schema definition order
3. Generate separator row
4. Generate one data row per card: `_id` first, then field values in schema order, with pipes and newlines escaped
5. Generate a new card `_id` using a random alphanumeric string if the card has no existing ID
6. Reconstruct the full block: opening fence, config, `---`, blank line, table, closing fence

**Write-back principle:** only the text range of the affected block is patched in the source file — surrounding content is never rewritten.

## Minimum Viable Board

```
```fancy-kanban
---
title: My Board
fields:
  - name: title, type: Text, label: Title
  - name: status, type: Select, options: inbox|doing|done, label: Status, default: inbox
workflow: inbox→doing, inbox→done, doing→done, doing→inbox, done→doing, done→inbox
---

| _id | Title | Status |
|-----|-------|--------|
```
```

## Compatibility Notes

- Without the plugin, the block renders as a plain code block — all data is still visible as plain text
- The markdown table inside the block is valid markdown and readable in any text editor
- The config section follows YAML conventions familiar to static site generators and note-taking apps
- Removing the plugin leaves all data intact and recoverable

## Deprecated Features

See [docs/deprecations.md](deprecations.md) for the full list of deprecated field types and config keys, their replacements, and planned removal versions.

Files in this skill

  • CHANGELOG.md10.7 KB
  • SKILL.md14 KB
  • codecov.yml286 B
  • docs/css-customization.md4.1 KB
  • docs/deprecations.md1.3 KB
  • docs/schema.md13.8 KB
  • esbuild.config.mjs1.2 KB
  • images/fancy-kanban.png4 KB
  • main.js77.8 KB
  • main.ts4.4 KB
  • manifest.json362 B
  • package.json1.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…