Pure TailwindMIT licensed

Nutrition Facts Label in Tailwind CSS

An FDA-style Nutrition Facts panel rebuilt as one semantic table: nutrient names are row headers, the % Daily Value column is a real column header, and the label's famous rule hierarchy — 10px, 5px and 1px bars — is just border-t-[10px], border-t-[5px] and border-t. Set in Libre Franklin, the closest open match to the label's Helvetica, with the calorie figure fluid via clamp().

Live Demo
Plain CSSTailwind v4
Try it

Mosaic by Cruip: a complete Tailwind admin dashboard template in HTML, React, Next.js and Vue · $69

Cruip · Mosaic by Cruip · Affiliate link

View Mosaic dashboard

The code

<section class="ttb-11 grid-cols-1 w-full min-h-svh grid place-items-center bg-ttb11-bg px-5 py-14 font-ttb11-sans text-black" aria-label="Nutrition facts label demo">
  <div class="grid w-full justify-items-center gap-6">
    <div class="max-w-sm text-center">
      <p class="text-[11px] font-bold tracking-[0.2em] text-ttb11-acc uppercase">Cold-pressed · 240 mL</p>
      <h2 class="mt-1.5 text-2xl font-black tracking-[-0.02em]">Avocado Green Smoothie</h2>
    </div>
    <div class="w-full max-w-[320px] border-[3px] border-black bg-white p-2 shadow-[8px_8px_0_oklch(0_0_0/0.14)]">
      <table class="w-full border-collapse text-[13px] tabular-nums">
        <caption class="border-b border-black pb-1 text-left">
          <span class="block text-[44px] leading-[0.9] font-black tracking-[-0.04em]">Nutrition Facts</span>
          <span class="mt-1.5 block text-[14px]">2 servings per container</span>
          <span class="flex justify-between text-[15px] font-black"><span>Serving size</span><span>1 cup (240mL)</span></span>
        </caption>
        <tbody class="border-t-[10px] border-black">
          <tr><td colspan="2" class="pt-1 text-[11px] font-black">Amount per serving</td></tr>
          <tr class="align-baseline"><th scope="row" class="text-left text-[26px] leading-none font-black">Calories</th><td class="text-right text-[clamp(44px,14vw,52px)] leading-none font-black">280</td></tr>
        </tbody>
        <tbody class="border-t-[5px] border-black [&_td]:py-[3px] [&_td]:text-right [&_td]:font-bold [&_th]:py-[3px] [&_th]:text-left [&_th]:font-normal [&_tr+tr]:border-t [&_tr+tr]:border-black">
          <tr><td colspan="2" class="text-[11px] font-bold">% Daily Value*</td></tr>
          <tr><th scope="row"><b class="font-black">Total Fat</b> 14g</th><td>18%</td></tr>
          <tr><th scope="row" class="pl-4!">Saturated Fat 2g</th><td>10%</td></tr>
          <tr><th scope="row" class="pl-4!"><i>Trans</i> Fat 0g</th><td></td></tr>
          <tr><th scope="row"><b class="font-black">Cholesterol</b> 0mg</th><td>0%</td></tr>
          <tr><th scope="row"><b class="font-black">Sodium</b> 85mg</th><td>4%</td></tr>
          <tr><th scope="row"><b class="font-black">Total Carbohydrate</b> 32g</th><td>12%</td></tr>
          <tr><th scope="row" class="pl-4!">Dietary Fiber 6g</th><td>21%</td></tr>
          <tr><th scope="row" class="pl-4!">Total Sugars 18g</th><td></td></tr>
          <tr><th scope="row" class="pl-8!">Includes 10g Added Sugars</th><td>20%</td></tr>
          <tr><th scope="row"><b class="font-black">Protein</b> 5g</th><td></td></tr>
        </tbody>
        <tbody class="border-t-[10px] border-black [&_td]:py-[3px] [&_td]:text-right [&_th]:py-[3px] [&_th]:text-left [&_th]:font-normal [&_tr+tr]:border-t [&_tr+tr]:border-black">
          <tr><th scope="row">Vitamin D 2mcg</th><td>10%</td></tr>
          <tr><th scope="row">Calcium 260mg</th><td>20%</td></tr>
          <tr><th scope="row">Iron 8mg</th><td>45%</td></tr>
          <tr><th scope="row">Potassium 640mg</th><td>15%</td></tr>
        </tbody>
        <tfoot class="border-t-[5px] border-black">
          <tr><td colspan="2" class="pt-1.5 text-[10px] leading-snug">* The % Daily Value (DV) tells you how much a nutrient in a serving of food contributes to a daily diet. 2,000 calories a day is used for general nutrition advice.</td></tr>
        </tfoot>
      </table>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-ttb11-bg: #f1ece4;
  --color-ttb11-bg: oklch(0.94 0.013 80);
  --color-ttb11-acc: #3f7a2e;
  --color-ttb11-acc: oklch(0.52 0.12 140);
  --font-ttb11-sans: 'Libre Franklin', 'Helvetica Neue', Arial, sans-serif;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.ttb-11 {
  width: 100%;
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-ttb11-bg: #f1ece4;
  --color-ttb11-bg: oklch(0.94 0.013 80);
  --color-ttb11-acc: #3f7a2e;
  --color-ttb11-acc: oklch(0.52 0.12 140);
  --font-ttb11-sans: 'Libre Franklin', 'Helvetica Neue', Arial, sans-serif;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.ttb-11 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@400..900&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • Three .ct-11__bar--* divider divs and three separate tables collapsed into one table whose <tbody> groups carry border-t-[10px] and border-t-[5px] — the rule hierarchy is now structure, not decoration.
  • tbody tr { border-top } tr:first-child { border-top:none } became the sibling variant [&_tr+tr]:border-t.
  • UI upgrade from the CSS original: nutrients are <th scope="row"> headers, so assistive tech reads name, amount and %DV together; the title, serving size and calories live in a real <caption> and <tbody>.
  • UI upgrade: the decorative scanning line was removed (it read as a loading state on a static label), Arial Black was replaced by Libre Franklin 900 so weights render on Linux, the calorie figure is fluid via clamp(), and the 🥑 emoji heading became a typographic product line.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Nutrition Facts Label in Tailwind CSS
Source: https://codefronts.com/snippets/tailwind-css-tables/nutrition-facts-label-tailwind-css/

An FDA-style Nutrition Facts panel rebuilt as one semantic table: nutrient names are row headers, the % Daily Value column is a real column header, and the label's famous rule hierarchy — 10px, 5px and 1px bars — is just border-t-[10px], border-t-[5px] and border-t. Set in Libre Franklin, the closest open match to the label's Helvetica, with the calorie figure fluid via clamp().
## HTML
```html
<section class="ttb-11 grid-cols-1 w-full min-h-svh grid place-items-center bg-ttb11-bg px-5 py-14 font-ttb11-sans text-black" aria-label="Nutrition facts label demo">
  <div class="grid w-full justify-items-center gap-6">
    <div class="max-w-sm text-center">
      <p class="text-[11px] font-bold tracking-[0.2em] text-ttb11-acc uppercase">Cold-pressed · 240 mL</p>
      <h2 class="mt-1.5 text-2xl font-black tracking-[-0.02em]">Avocado Green Smoothie</h2>
    </div>
    <div class="w-full max-w-[320px] border-[3px] border-black bg-white p-2 shadow-[8px_8px_0_oklch(0_0_0/0.14)]">
      <table class="w-full border-collapse text-[13px] tabular-nums">
        <caption class="border-b border-black pb-1 text-left">
          <span class="block text-[44px] leading-[0.9] font-black tracking-[-0.04em]">Nutrition Facts</span>
          <span class="mt-1.5 block text-[14px]">2 servings per container</span>
          <span class="flex justify-between text-[15px] font-black"><span>Serving size</span><span>1 cup (240mL)</span></span>
        </caption>
        <tbody class="border-t-[10px] border-black">
          <tr><td colspan="2" class="pt-1 text-[11px] font-black">Amount per serving</td></tr>
          <tr class="align-baseline"><th scope="row" class="text-left text-[26px] leading-none font-black">Calories</th><td class="text-right text-[clamp(44px,14vw,52px)] leading-none font-black">280</td></tr>
        </tbody>
        <tbody class="border-t-[5px] border-black [&_td]:py-[3px] [&_td]:text-right [&_td]:font-bold [&_th]:py-[3px] [&_th]:text-left [&_th]:font-normal [&_tr+tr]:border-t [&_tr+tr]:border-black">
          <tr><td colspan="2" class="text-[11px] font-bold">% Daily Value*</td></tr>
          <tr><th scope="row"><b class="font-black">Total Fat</b> 14g</th><td>18%</td></tr>
          <tr><th scope="row" class="pl-4!">Saturated Fat 2g</th><td>10%</td></tr>
          <tr><th scope="row" class="pl-4!"><i>Trans</i> Fat 0g</th><td></td></tr>
          <tr><th scope="row"><b class="font-black">Cholesterol</b> 0mg</th><td>0%</td></tr>
          <tr><th scope="row"><b class="font-black">Sodium</b> 85mg</th><td>4%</td></tr>
          <tr><th scope="row"><b class="font-black">Total Carbohydrate</b> 32g</th><td>12%</td></tr>
          <tr><th scope="row" class="pl-4!">Dietary Fiber 6g</th><td>21%</td></tr>
          <tr><th scope="row" class="pl-4!">Total Sugars 18g</th><td></td></tr>
          <tr><th scope="row" class="pl-8!">Includes 10g Added Sugars</th><td>20%</td></tr>
          <tr><th scope="row"><b class="font-black">Protein</b> 5g</th><td></td></tr>
        </tbody>
        <tbody class="border-t-[10px] border-black [&_td]:py-[3px] [&_td]:text-right [&_th]:py-[3px] [&_th]:text-left [&_th]:font-normal [&_tr+tr]:border-t [&_tr+tr]:border-black">
          <tr><th scope="row">Vitamin D 2mcg</th><td>10%</td></tr>
          <tr><th scope="row">Calcium 260mg</th><td>20%</td></tr>
          <tr><th scope="row">Iron 8mg</th><td>45%</td></tr>
          <tr><th scope="row">Potassium 640mg</th><td>15%</td></tr>
        </tbody>
        <tfoot class="border-t-[5px] border-black">
          <tr><td colspan="2" class="pt-1.5 text-[10px] leading-snug">* The % Daily Value (DV) tells you how much a nutrient in a serving of food contributes to a daily diet. 2,000 calories a day is used for general nutrition advice.</td></tr>
        </tfoot>
      </table>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-ttb11-bg: #f1ece4;
  --color-ttb11-bg: oklch(0.94 0.013 80);
  --color-ttb11-acc: #3f7a2e;
  --color-ttb11-acc: oklch(0.52 0.12 140);
  --font-ttb11-sans: 'Libre Franklin', 'Helvetica Neue', Arial, sans-serif;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
   stage is sized before the browser build compiles. */

.ttb-11 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

The label is a table now. The original drew bars as empty divs between three separate tables; here one <table> carries everything and each section break is a top border on the first row of a <tbody>:

<tbody class="border-t-[10px] border-black">  <!-- calories -->
<tbody class="border-t-[5px] border-black [&_tr+tr]:border-t">  <!-- nutrients -->

Indentation is semantic. Sub-nutrients use pl-4 / pl-8 on their row header, matching the label's visual nesting without nested tables.

Exact typography: font-black tracking-[-0.04em] leading-[0.9] for the title, tabular-nums so the DV column aligns, and footnotes at 10px — the one place a label legitimately runs small.

Make it yours

  • Dark packaging: swap bg-white/border-black/text-black for their inverses on the label.
  • Allergens: add a <tbody class="border-t-[10px]"> with a single bold "Contains:" row.
  • Dual-column labels: add a second value column and a <th scope="col"> per serving size.
  • Count-up calories: add data-count to the number and animate it with requestAnimationFrame.

Gotchas — read before shipping

  • This is a styling reference, not a compliance tool — real labels must follow 21 CFR 101.9 for sizes, rounding and required nutrients.
  • Libre Franklin loads from Google Fonts; the Arial / Helvetica fallback is metrically close but slightly wider.
  • border-t on <tbody> only paints with border-collapse — keep it on the table.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Plain table borders and type — the floor is Tailwind v4's generated sheet.

Techniques used in this demo

Arbitraryneeds bracket syntax

Search CodeFronts

Loading…