ComponentsData display

Board

A Kanban board. Columns go across. Lanes go down. Each card sits in the cell where its column and lane meet. Drag cards to any cell, drag columns by their grip, and click a card to open it.

Lanes by16 cards
Over limit
T1Import last season's seed orders
Urgent1 pt
T13Compost temperature log
Urgent3 pt
T9Archive 2024 field notes
Urgent5 pt
T5Export harvest report to CSV
Urgent8 pt
T16Soil test results by bed
High1 pt
T12Onboarding guide for seasonal staff
High2 pt
T8Label printer for seed packets
High3 pt
T4Watering schedule by bed
High5 pt
T11Map of north field beds
Normal1 pt
T7Frost alert text messages
Normal2 pt
T3Fix duplicate invoices for wholesale buyers
Normal3 pt
T15Delivery route for Saturday
Normal8 pt
T6Supplier contact sheet
Low1 pt
T2Greenhouse sensor dashboard
Low2 pt
T14Bulk edit plant varieties
Low5 pt
T10Price list for the spring market
Low8 pt

Click a card to open it.

Usage

A board with lanesTSX
import { Board, type BoardColumn, type BoardItem } from "@tulipfarm/sprout/board";

const columns: BoardColumn[] = [
  { id: "todo", title: "To do" },
  { id: "doing", title: "In progress", limit: 3 },
  { id: "done", title: "Done" },
];

const [items, setItems] = useState<BoardItem[]>([
  { id: "1", title: "Frost alert texts", columnId: "todo", laneId: "ana" },
  { id: "2", title: "Seed order import", columnId: "doing", laneId: "ben" },
]);

<Board
  columns={columns}
  lanes={[{ id: "ana", title: "Ana" }, { id: "ben", title: "Ben" }]}
  items={items}
  onItemsChange={setItems}
  onItemAdd={({ title, columnId, laneId }) => addCard(title, columnId, laneId)}
  onItemOpen={(item) => openSheet(item)}
  renderBody={(item) => <Badge>{item.priority}</Badge>}
  searchable
/>

  • columns: { id, title, limit? }[]. Overlimit, the header turns amber and says “Over limit”.
  • lanes: { id, title }[]. Leave it out for a plain board with no lane bars.
  • items: { id, columnId, laneId?, title }[]plus your own fields. Cards in a cell show in their order initems.
  • onItemsChange: gets the whole new list after a move, a rename, or a column delete. Without it, cards do not move.
  • onColumnsChange: gets the new columns after a reorder, rename, or delete. Without it, columns are fixed.
  • onItemAdd and onColumnAdd: show “Add card” in every cell and “Add column” after the last column. Your app makes the id.
  • onItemOpen, renderBody, itemActions: what a click does, the card body under the title, and the items in the card menu.
  • renderTitle and renderCard: change the title, or draw the whole card. See “Custom cards”.
  • searchable, searchText, laneBy: the toolbar. Search filters by title and any text you add. “Lanes by” is a select; your app regroups lanes andlaneId.

Without lanes

With no lanes, the board is one row and the cells fill its height.

Import last season's seed orders
Urgent1 ptA
Supplier contact sheet
Low1 ptD
Greenhouse sensor dashboard
Low2 ptD
Frost alert text messages
Normal2 ptC
Fix duplicate invoices for wholesale buyers
Normal3 ptC
Label printer for seed packets
High3 ptB
Watering schedule by bed
High5 ptB
Archive 2024 field notes
Urgent5 ptA

Custom cards

renderTitle changes only the title. The playground above uses it to show a key before each title. Screen readers still readitem.title.

renderCard draws the whole card. The board removes its own frame and gives you parts.title and parts.menu. Put them where you want. They keep rename and the card menu working. The board still owns drag, focus, keys, click, and selection.

TSX
<Board
  columns={columns}
  items={items}
  onItemsChange={setItems}
  renderCard={(item, parts) => (
    <div className="flex gap-3 rounded-lg border p-2">
      <EntityAvatar size="sm" name={item.assignee} />
      <div className="flex-1">
        <div className="flex items-start gap-1">
          {parts.title}
          {parts.menu}
        </div>
        <span className="text-xs">{item.points} pt</span>
      </div>
    </div>
  )}
/>

Backlog
Ready
In progress
A
Import last season's seed orders
Ana Ruiz · 1 pt
D
Supplier contact sheet
Dara Singh · 1 pt
D
Greenhouse sensor dashboard
Dara Singh · 2 pt
C
Frost alert text messages
Chen Wei · 2 pt
C
Fix duplicate invoices for wholesale buyers
Chen Wei · 3 pt
B
Label printer for seed packets
Ben Okafor · 3 pt

Large boards

50 columns, 20 lanes, and 10,000 cards. Only the lanes and cards in view render. The card you drag always stays rendered.

10,000 cards
Stage 1
Stage 2
Stage 3
Stage 4
Stage 5
Stage 6
Stage 7
Stage 8
Stage 9
Stage 10
Stage 11
Stage 12
Stage 13
Stage 14
Stage 15
Stage 16
Stage 17
Stage 18
Stage 19
Stage 20
Stage 21
Stage 22
Stage 23
Stage 24
Stage 25
Stage 26
Stage 27
Stage 28
Stage 29
Stage 30
Stage 31
Stage 32
Stage 33
Stage 34
Stage 35
Stage 36
Stage 37
Stage 38
Stage 39
Stage 40
Stage 41
Stage 42
Stage 43
Stage 44
Stage 45
Stage 46
Stage 47
Stage 48
Stage 49
Stage 50
Card 1
Card 101
Card 201
Card 301
Card 401
Card 501
Card 601
Card 701
Card 801
Card 901
Card 1001
Card 1101
Card 1201
Card 1301
Card 81
Card 181
Card 281
Card 381
Card 481
Card 581
Card 681
Card 781
Card 881
Card 981
Card 1081
Card 1181
Card 1281
Card 1381
Card 61
Card 161
Card 261
Card 361
Card 461
Card 561
Card 661
Card 761
Card 861
Card 961
Card 1061
Card 1161
Card 1261
Card 1361
Card 41
Card 141
Card 241
Card 341
Card 441
Card 541
Card 641
Card 741
Card 841
Card 941
Card 1041
Card 1141
Card 1241
Card 1341
Card 21
Card 121
Card 221
Card 321
Card 421
Card 521
Card 621
Card 721
Card 821
Card 921
Card 1021
Card 1121
Card 1221
Card 1321
Card 38
Card 138
Card 238
Card 338
Card 438
Card 538
Card 638
Card 738
Card 838
Card 938
Card 1038
Card 1138
Card 1238
Card 1338
Card 18
Card 118
Card 218
Card 318
Card 418
Card 518
Card 618
Card 718
Card 818
Card 918
Card 1018
Card 1118
Card 1218
Card 1318
Card 98
Card 198
Card 298
Card 398
Card 498
Card 598
Card 698
Card 798
Card 898
Card 998
Card 1098
Card 1198
Card 1298
Card 1398
Card 78
Card 178
Card 278
Card 378
Card 478
Card 578
Card 678
Card 778
Card 878
Card 978
Card 1078
Card 1178
Card 1278
Card 1378
Card 58
Card 158
Card 258
Card 358
Card 458
Card 558
Card 658
Card 758
Card 858
Card 958
Card 1058
Card 1158
Card 1258
Card 1358
Card 95
Card 195
Card 295
Card 395
Card 495
Card 595
Card 695
Card 795
Card 895
Card 995
Card 1095
Card 1195
Card 1295
Card 1395
Card 75
Card 175
Card 275
Card 375
Card 475
Card 575
Card 675
Card 775
Card 875
Card 975
Card 1075
Card 1175
Card 1275
Card 1375
Card 55
Card 155
Card 255
Card 355
Card 455
Card 555
Card 655
Card 755
Card 855
Card 955
Card 1055
Card 1155
Card 1255
Card 1355
Card 35
Card 135
Card 235
Card 335
Card 435
Card 535
Card 635
Card 735
Card 835
Card 935
Card 1035
Card 1135
Card 1235
Card 1335
Card 15
Card 115
Card 215
Card 315
Card 415
Card 515
Card 615
Card 715
Card 815
Card 915
Card 1015
Card 1115
Card 1215
Card 1315
Card 52
Card 152
Card 252
Card 352
Card 452
Card 552
Card 652
Card 752
Card 852
Card 952
Card 1052
Card 1152
Card 1252
Card 1352
Card 32
Card 132
Card 232
Card 332
Card 432
Card 532
Card 632
Card 732
Card 832
Card 932
Card 1032
Card 1132
Card 1232
Card 1332
Card 12
Card 112
Card 212
Card 312
Card 412
Card 512
Card 612
Card 712
Card 812
Card 912
Card 1012
Card 1112
Card 1212
Card 1312
Card 92
Card 192
Card 292
Card 392
Card 492
Card 592
Card 692
Card 792
Card 892
Card 992
Card 1092
Card 1192
Card 1292
Card 1392
Card 72
Card 172
Card 272
Card 372
Card 472
Card 572
Card 672
Card 772
Card 872
Card 972
Card 1072
Card 1172
Card 1272
Card 1372
Card 9
Card 109
Card 209
Card 309
Card 409
Card 509
Card 609
Card 709
Card 809
Card 909
Card 1009
Card 1109
Card 1209
Card 1309
Card 89
Card 189
Card 289
Card 389
Card 489
Card 589
Card 689
Card 789
Card 889
Card 989
Card 1089
Card 1189
Card 1289
Card 1389
Card 69
Card 169
Card 269
Card 369
Card 469
Card 569
Card 669
Card 769
Card 869
Card 969
Card 1069
Card 1169
Card 1269
Card 1369
Card 49
Card 149
Card 249
Card 349
Card 449
Card 549
Card 649
Card 749
Card 849
Card 949
Card 1049
Card 1149
Card 1249
Card 1349
Card 29
Card 129
Card 229
Card 329
Card 429
Card 529
Card 629
Card 729
Card 829
Card 929
Card 1029
Card 1129
Card 1229
Card 1329
Card 66
Card 166
Card 266
Card 366
Card 466
Card 566
Card 666
Card 766
Card 866
Card 966
Card 1066
Card 1166
Card 1266
Card 1366
Card 46
Card 146
Card 246
Card 346
Card 446
Card 546
Card 646
Card 746
Card 846
Card 946
Card 1046
Card 1146
Card 1246
Card 1346
Card 26
Card 126
Card 226
Card 326
Card 426
Card 526
Card 626
Card 726
Card 826
Card 926
Card 1026
Card 1126
Card 1226
Card 1326
Card 6
Card 106
Card 206
Card 306
Card 406
Card 506
Card 606
Card 706
Card 806
Card 906
Card 1006
Card 1106
Card 1206
Card 1306
Card 86
Card 186
Card 286
Card 386
Card 486
Card 586
Card 686
Card 786
Card 886
Card 986
Card 1086
Card 1186
Card 1286
Card 1386
Card 23
Card 123
Card 223
Card 323
Card 423
Card 523
Card 623
Card 723
Card 823
Card 923
Card 1023
Card 1123
Card 1223
Card 1323
Card 3
Card 103
Card 203
Card 303
Card 403
Card 503
Card 603
Card 703
Card 803
Card 903
Card 1003
Card 1103
Card 1203
Card 1303
Card 83
Card 183
Card 283
Card 383
Card 483
Card 583
Card 683
Card 783
Card 883
Card 983
Card 1083
Card 1183
Card 1283
Card 1383
Card 63
Card 163
Card 263
Card 363
Card 463
Card 563
Card 663
Card 763
Card 863
Card 963
Card 1063
Card 1163
Card 1263
Card 1363
Card 43
Card 143
Card 243
Card 343
Card 443
Card 543
Card 643
Card 743
Card 843
Card 943
Card 1043
Card 1143
Card 1243
Card 1343
Card 80
Card 180
Card 280
Card 380
Card 480
Card 580
Card 680
Card 780
Card 880
Card 980
Card 1080
Card 1180
Card 1280
Card 1380
Card 60
Card 160
Card 260
Card 360
Card 460
Card 560
Card 660
Card 760
Card 860
Card 960
Card 1060
Card 1160
Card 1260
Card 1360
Card 40
Card 140
Card 240
Card 340
Card 440
Card 540
Card 640
Card 740
Card 840
Card 940
Card 1040
Card 1140
Card 1240
Card 1340
Card 20
Card 120
Card 220
Card 320
Card 420
Card 520
Card 620
Card 720
Card 820
Card 920
Card 1020
Card 1120
Card 1220
Card 1320
Card 100
Card 200
Card 300
Card 400
Card 500
Card 600
Card 700
Card 800
Card 900
Card 1000
Card 1100
Card 1200
Card 1300
Card 1400

Selecting and moving many cards

Cmd-click or Ctrl-click adds a card to the selection. Shift-click selects a range. Drag one selected card and the rest go with it. Escape clears the selection.

Accessibility

The board is a named region. Each cell is a list named by its column and lane. Tab moves to a card. Space picks it up, arrow keys move it, Space drops it, and Escape puts it back. A screen reader hears each step with the card title and its cell. On touch, press and hold a card to pick it up; a swipe still scrolls.

Enter opens a card. F2 or Rename in the card menu renames it. With noonItemOpen, a double-click renames too. In a title field, Enter saves and Escape cancels. Columns move with their grip button in the same way. The “Over limit” state is in words, not only color.

Theme tokens

Cards use --sprout-card-raised and--sprout-card-raised-shadow. Columns tint--sprout-hover over --sprout-canvas. Over limit uses --sprout-warning-subtle, and selection--sprout-primary. Set --sprout-board-height,--sprout-board-column-width, and--sprout-board-cell-height on the board to size it.