Lattice Grid Buy a licence

demo D220

Open positions board in ESM

A recruitment dashboard screen: a progress bar, a status pill and a templated kebab, on a card

render: 'progress' · 'pill' · cell.template

This is a recruitment 'Open positions' board, the kind of screen a hiring team works in all day: each role shows how full its pipeline is as a bar, its state as a coloured status pill, and a row menu for actions like pause or close. It is the standard grid wearing your product's own look, taking its colours, spacing and type from the theme so it drops cleanly into a polished application.

Loading a live grid…

This is the ES module version. The grid is imported straight from a CDN as a module, the same configuration as the vanilla tab without a global script tag, so it drops into any bundler or a bare module script.

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.108.0/lattice-grid.min.css">

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.108.0/lattice-grid.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&display=swap">
<style>
  body { font-family: "IBM Plex Sans", system-ui, sans-serif; background: #EFEDE4; padding: 34px; }
  .card { background: #FBFAF5; border-radius: 20px; padding: 22px 24px 12px;
    box-shadow: 0 1px 2px rgba(20,22,28,.04), 0 18px 46px -30px rgba(20,22,28,.4); }
  .card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
  .card-head h2 { font-size: 1.25rem; font-weight: 700; margin: 0; }

  /* The grid adopts the card's palette through the theme's real token names. */
  .lattice {
    --lattice-background: transparent;
    --lattice-border-color: #EDEAE0;
    --lattice-header-foreground: #8C8A80;
    --lattice-accent: #F0A94C;
    /* Re-tint the four built-in pill variants. The pill renderer reads its
       variant from its own props, so these tokens are the extension point. */
    --lattice-variant-success-fill: #DEF7EC; --lattice-variant-success-text: #036C4F;
    --lattice-variant-info-fill: #E1EFFE;    --lattice-variant-info-text: #1A56DB;
    --lattice-variant-accent-fill: #EDE9FE;  --lattice-variant-accent-text: #6D28D9;
    --lattice-variant-warning-fill: #FEF0DC; --lattice-variant-warning-text: #92400E;
  }
  .lattice .lat-header-cell, .lattice .lat-cell { border-right: 0; }
  .lattice .is-strong { font-weight: 600; }
  /* props.variant is written out as data-variant, so keying off it here is what
     makes the amber progress bar its own colour over a hatched track. */
  .lattice [data-variant="amber"] .lat-progress-track {
    background-image: repeating-linear-gradient(-45deg, #E7EBF3 0 4px, #F4F6FA 4px 8px);
    background-color: #F4F6FA; border-radius: 999px; height: 12px;
  }
  .lattice [data-variant="amber"] .lat-progress-fill { background: #F5B863; border-radius: 999px; }
  .lattice .kebab { font: inherit; font-size: 18px; color: #8C8A80; background: none; border: 0; cursor: pointer; }
  .lattice .btn { font: inherit; font-size: .82rem; font-weight: 600; border-radius: 999px; padding: 7px 14px; cursor: pointer; margin-right: 8px; }
  .lattice .btn--primary { background: #F5B863; color: #4A3410; border: 0; }
  .lattice .btn--ghost { background: transparent; color: #17181C; border: 1px solid #E2DFD2; }
</style>

<div class="card">
  <div class="card-head"><h2>Open Job Positions</h2></div>
  <div id="jobs"></div>
</div>

<script type="module">
  import { createGrid } from 'https://cdn.latticegrid.dev/lattice-grid@1.108.0/lattice-grid.esm.min.js';

  const jobs = [
    { id: '1', title: 'Senior Frontend Developer', dept: 'Engineering', fill: 72, location: 'San Francisco, CA', applications: 24, status: 'Open' },
    { id: '2', title: 'Product Manager', dept: 'Product', fill: 45, location: 'New York, NY', applications: 18, status: 'Open' },
    { id: '3', title: 'UX Designer', dept: 'Design', fill: 30, location: 'Austin, TX', applications: 31, status: 'Interviewing' },
    // …
  ];

  createGrid(document.getElementById('jobs'), {
    rowKey: 'id',
    density: 'spacious',               // the generous row rhythm
    showColumnFunctions: false,        // quiet headers, no sort/filter/menu furniture
    autoHeight: true,
    columns: [
      { field: 'title', title: 'Job title', layout: { width: 280 }, cell: { class: 'is-strong' } },
      { field: 'fill', title: 'Department', sort: { enabled: false }, layout: { width: 230 },
        cell: { render: 'progress', props: { min: 0, max: 100, showValue: false, variant: 'amber', label: 'Pipeline filled' } } },
      { field: 'dept', title: ' ', sort: { enabled: false }, layout: { width: 140 }, header: { render: () => '' } },
      { field: 'location', title: 'Location', layout: { width: 180 } },
      { field: 'applications', title: 'Applications', type: 'number', total: 'sum', layout: { width: 130 } },
      { field: 'status', title: 'Status', layout: { width: 150 },
        cell: { render: 'pill', props: { shape: 'pill',
          variant: { map: { Open: 'success', Interviewing: 'info', Offer: 'accent', Paused: 'warning' }, default: 'neutral' } } } },
      { id: 'act', title: 'Action', sort: { enabled: false }, layout: { width: 90 },
        cell: { template: '<button class="kebab" aria-label="Row actions">⋮</button>' } },
    ],
    rows: jobs,
  });
</script>