USWDS + Tailwind CSS

website·Indexed 32 days ago

https://uswds-tailwind.com

A project designed to help federal product teams work with, adjust, and extend the U.S. Web Design System (USWDS) using Tailwind CSS. It provides a comprehensive library of components—including banners, identifiers, modals, and form elements—along with guidance on typography, colors, icons, and JavaScript integration via Alpine.js.

Tokens
43.7K
Snippets
117
Records
169
Agent score
65%

What's inside USWDS Tailwind

  1. Overview of USWDS-Tailwind design system

    USWDS-Tailwind is a comprehensive design system for building federal websites and applications. It combines the US Web Design System (USWDS) with the speed and flexibility of Tailwind CSS, Alpine.js, and Parcel. Key features include strict adherence to WAI-ARIA and Section 508 accessibility standards, tokenized components for customization, support for multiple color modes (including high-contrast), and a lightweight footprint optimized for user experience.
  2. Overview of the USWDS-Tailwind project

    The USWDS-Tailwind project is an experimental modernization of the US Web Design System (USWDS). It replaces older tools like BEM naming, SCSS, Gulp, and manual font/icon management with a utility-first approach. The goal is to make it easier for federal product teams to work with, adjust, and extend the USWDS by combining Tailwind CSS's utility-first framework with USWDS design tokens.
  3. Implement vertical cards with various media layouts

    Vertical cards are used to group content and actions about a single subject. They are typically implemented using a flex-column layout. Common variations include placing the image at the top, placing the header first, or using inset/exdent media styles. For responsive grids, use container queries (e.g., @tablet:col-span-3) to adjust the number of cards per row.
    <ul class="@container grid grid-cols-6 gap-y-8 gap-x-4">
      <!-- Default Vertical Card -->
      <li class="bg-white flex flex-col col-span-6 @tablet:col-span-3 @desktop:col-span-2">
        <div class="border-x-2 border-t-2 border-gray-10 rounded-t px-6 pt-6 pb-2">
          <h2 class="font-bold font-merriweather text-lg">Card</h2>
        </div>
        <div class="border-x-2 border-gray-10 px-6 py-2 grow">
          <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
        </div>
        <div class="border-x-2 border-b-2 border-gray-10 rounded-b px-6 pb-6 pt-2">
          <button class="rounded font-bold leading-none text-white px-5 py-3 bg-blue-60v hover:bg-blue-warm-70v active:bg-blue-warm-80v focus:outline focus:outline-4 focus:outline-offset-4 focus:outline-blue-40v">Visit Florida Keys</button>
        </div>
      </li>
    </ul>
  4. Implement a US Government Official Banner

    Banners are used to identify official US government websites and help visitors verify security. This component relies on the x-collapse JavaScript plugin for its interactive functionality. It is structured using a <section> landmark for accessibility, a trigger button, and a collapsible content area.
    <div class="space-y-8">
      <!-- .gov Banner Example -->
      <section x-collapse class="@container bg-gray-5 group" aria-label="Official website of the United States government">
        <header class="flex text-xs gap-2 @tablet:items-center pl-4 pr-12 @tablet:pr-4 @tablet:px-8 py-2 @tablet:py-1 mx-auto max-w-5xl min-h-12 @tablet:min-h-0 leading-tight @tablet:leading-none relative">
          <div class="hidden group-[[data-open]]:flex @tablet:group-[[data-open]]:hidden items-center justify-center absolute right-0 inset-y-0 bg-gray-10 size-12">
            <span class="icon-[material-symbols--close] text-blue-60v size-6"></span>
          </div>
          <div class="pt-0.5 @tablet:pt-0 shrink-0 w-4">
            <!-- US Flag SVG -->
            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 64 64"><path fill="#DB3E1F" d="M32 10h32v4H32zM32 18h32v4H32zM32 26h32v4H32zM32 34h32v4H32zM0 42h64v4H0zM0 50h64v4H0z"/><path fill="#fff" d="M32 14h32v4H32zM32 22h32v4H32zM32 30h32v4H32z"/><path fill="#fff" d="M32 30h32v4H32zM0 46h64v4H0zM0 38h64v4H0z"/><path fill="#fff" d="M0 38h64v4H0z"/><path fill="#1D33B1" d="M0 10h32v28H0z"/><path fill="#fff" d="M4 14h4v4H4zM8 22h4v4H8zM4 30h4v4H4zM12 14h4v4h-4zM16 22h4v4h-4zM12 30h4v4h-4zM20 14h4v4h-4zM24 22h4v4h-4zM20 30h4v4h-4z"/></svg>
          </div>
          <div class="@tablet:flex gap-2 items-center">
            <div>
              <p>An official website of the United States government</p>
            </div>
            <button type="button" x-collapse:trigger class="text-blue-60v group-[[data-open]]:block focus:outline-none @tablet:focus:outline @tablet:focus:outline-4 @tablet:focus:outline-blue-40v cursor-pointer group">
              <div class="inline-flex items-center underline group-[[data-open]]:hidden @tablet:group-[[data-open]]:inline-flex">
                <span>Here&#8217;s how you know</span>
                <span class="icon-[material-symbols--expand-more] size-4 align-middle group-aria-expanded:rotate-180"></span>
              </div>
            </button>
          </div>
        </header>
        <div x-cloak x-collapse:content class="py-6 px-6 grid @tablet:grid-cols-2 gap-6 mx-auto max-w-5xl">
          <div class="flex gap-2">
            <div aria-hidden="true" class="rounded-full border border-blue-50 text-blue-50 size-10 shrink-0 justify-center items-center flex">
              <span class="icon-[material-symbols--account-balance] size-5 align-middle"></span>
            </div>
            <div>
              <p><span class="font-bold">Official websites use .gov</span></p>
              <p>A <span class="font-bold">.gov</span> website belongs to an official government organization in the United States.</p>
            </div>
          </div>
          <div class="flex gap-2">
            <div aria-hidden="true" class="rounded-full border border-green-40v text-green-40v size-10 shrink-0 justify-center items-center flex">
              <span class="icon-[material-symbols--lock] size-5 align-middle"></span>
            </div>
            <div>
              <p><span class="font-bold">Secure .gov websites use HTTPS</span></p>
              <p>A <span class="font-bold">lock</span> (<span class="icon-[material-symbols--lock] size-4 align-middle"></span>) or <span class="font-bold">https://</span> means you've safely connected to the .gov website.</p>
            </div>
          </div>
        </div>
      </section>
    </div>
  5. Implement a Character Count component with Alpine.js

    The Character Count component helps users track how many characters they can enter into a field. It requires the Character Count JavaScript plugin. The implementation uses a root container and specific Alpine.js directives to synchronize the input limit with visual and screen-reader-accessible status messages.
    <div class="max-w-xs" x-character-count>
      <label for="input-limit" class="block">Text input</label>
      <div>
        <span id="input-hint" class="text-gray-50">This is an input with a character counter.</span>
      </div>
      <div class="mt-2 relative">
        <input 
          x-character-count:input 
          maxlength="25" 
          id="input-limit" 
          aria-describedby="input-hint character-count-hint" 
          class="p-2 w-full max-w-lg h-10 border border-gray-60 focus:outline focus:outline-offset-0 focus:outline-4 focus:outline-blue-40v invalid:ring-4 invalid:ring-red-60v invalid:border-transparent invalid:outline-offset-4 valid:ring-4 valid:ring-green-cool-40v valid:border-transparent valid:outline-offset-4"
        />
      </div>
      <div class="pt-2">
        <!-- Static hint for progressive enhancement -->
        <span id="character-count-hint" class="sr-only">You can enter up to 25 characters</span>
        <!-- Visual status updated by Alpine -->
        <span x-character-count:status aria-hidden="true" class="text-gray-50 invalid:text-red-60v invalid:font-bold">You can enter up to 25 characters</span>
        <!-- Screen reader status updated by Alpine -->
        <span x-character-count:sr-status aria-live="polite" class="sr-only">You can enter up to 25 characters</span>
      </div>
    </div>
  6. Implement a basic Modal using Alpine.js directives

    A modal disables page content to focus user attention on a single task. It requires the Modal JavaScript plugin and uses specific Alpine.js directives to manage state and accessibility. The modal structure typically consists of a root container, a trigger button, a dialog wrapper, the content area, and a backdrop teleported to the body.
    <div x-modal>
      <!-- Trigger -->
      <button x-modal:trigger class="rounded font-bold text-white px-5 py-3 bg-blue-60v">Open default modal</button>
    
      <!-- Dialog Wrapper -->
      <div>
        <div x-cloak x-modal:dialog class="fixed inset-0 overflow-y-auto flex items-center justify-center p-4 z-50 animate-in ease-in-out duration-150 fade-in">
          <!-- Content Area -->
          <div x-modal:content class="@container relative w-full max-w-lg rounded-lg bg-white shadow-lg">
            <div class="p-8 pt-10">
              <h2 x-modal:title class="text-xl font-bold font-merriweather">Are you sure you want to continue?</h2>
              <div class="mt-4">
                <p x-modal:description class="mt-2 text-gray-600">You have unsaved changes that will be lost.</p>
              </div>
              <div class="mt-6">
                <ul class="flex flex-col @mobile-lg:flex-row gap-2">
                  <li>
                    <button x-modal:close-button type="button" class="rounded font-bold text-white px-5 py-3 bg-blue-60v">Continue without saving</button>
                  </li>
                  <li>
                    <button x-modal:close-button type="button" class="p-3 underline text-blue-60v bg-transparent">Go back</button>
                  </li>
                </ul>
              </div>
              <!-- Close Icon -->
              <button x-modal:close-button type="button" class="absolute top-0 right-0 p-1 text-gray-50 bg-transparent rounded" aria-label="Close this window">
                <div class="icon-[material-symbols--close] size-8 mt-0.5 mr-0.5 align-middle"></div>
              </button>
            </div>
          </div>
        </div>
        <!-- Backdrop -->
        <template x-teleport="body">
          <div x-cloak x-modal:backdrop class="fixed z-40 inset-0 bg-black/70 animate-in ease-in-out duration-150 fade-in"></div>
        </template>
      </div>
    </div>
  7. Using Tailwind CSS for USWDS implementation

    Tailwind CSS is used to implement USWDS components to improve performance and developer experience. Key benefits include:

    • Reduced CSS Footprint: Generates only necessary CSS, resulting in a build size of approximately 123 KB for USWDS components.
    • Specificity Resolution: By using utility classes instead of BEM and nested SCSS, developers avoid CSS specificity issues where utility classes (e.g., .text-white) are overridden by complex selectors (e.g., .usa-footer__secondary-link a).
    • Tooling: Integration with Tailwind DevTools for class sorting, linting, and autocomplete.
  8. Implement a Combo box component

    A combo box allows users to select an item from a large list of options. It uses a progressive enhancement strategy: it renders as a native <select> element for users without JavaScript, and transforms into a rich interactive component once the Combobox JavaScript plugin is loaded. The component uses custom Alpine.js directives to manage its state and accessibility.
    <div x-combobox class="max-w-lg">
      <label x-combobox:label for="combobox" class="block">Select a fruit</label>
      
      <!-- Fallback select for progressive enhancement -->
      <select id="combobox" x-combobox:values>
        <option value="apple">Apple</option>
        <option value="banana">Banana</option>
      </select>
    
      <!-- Enhanced UI rendered via Alpine.js -->
      <template x-if="$combobox.isLoaded">
        <div class="relative mt-2">
          <div class="flex w-full">
            <input x-combobox:input class="pr-10 p-2 bg-white w-full h-10 border border-gray-60">
            <div class="absolute z-10 inset-y-0 right-0 flex">
              <button x-combobox:clear class="h-full px-1 flex items-center bg-transparent">
                <div class="icon-[material-symbols--close] size-6 text-gray-50"></div>
              </button>
              <button x-combobox:toggle class="h-full px-1 flex items-center bg-transparent">
                <div class="icon-[material-symbols--expand-more] size-8 text-gray-50"></div>
              </button>
            </div>
          </div>
          
          <ul x-combobox:list class="absolute border border-gray-60 bg-white max-h-52 overflow-y-scroll w-full z-10">
            <template x-for="option in $combobox.allOptions" :key="option.value">
              <li x-combobox:item class="p-2 cursor-pointer aria-selected:bg-blue-60v aria-selected:text-white" x-text="option.label"></li>
            </template>
            <template x-if="$combobox.noResults">
              <li class="p-2 cursor-not-allowed">No results found</li>
            </template>
          </ul>
        </div>
      </template>
    </div>
  9. Ensure Button accessibility for assistive technologies

    To ensure buttons are accessible to screen readers and other assistive technologies, do not rely on color alone to communicate the button's intention. The purpose and action of the button must be obvious from the text content within the button itself.
  10. Initialize a USWDS + Tailwind project

    To start a new project with USWDS and Tailwind, create a project directory and initialize a npm project.
    mkdir my-project && cd my-project
    npm init -y
  11. Self-host fonts using Fontsource

    Fontsource is used to self-host fonts within projects. This provides granular control over font updates and rollbacks on a per-project basis, removing the dependency on external downloads or global USWDS updates.
  12. Format running text with the Prose component

    Use the .prose class to wrap blocks of running text. This ensures that standard HTML elements like headings (h1-h5), paragraphs (p), lists (ul, ol), and tables are styled consistently. The component is responsive and adapts to the following breakpoints: Mobile (320px), Mobile Lg (480px), Tablet (640px), and Desktop (1024px).
    <div class="prose">
      <h1>Page heading</h1>
      <p>Great Smoky Mountains National Park straddles the border of North Carolina and Tennessee.</p>
      
      <h2>Section heading</h2>
      <p>The sprawling landscape encompasses lush forests...</p>
      
      <ul>
        <li>Unordered list item
          <ul>
            <li>Nested unordered list item</li>
          </ul>
        </li>
      </ul>
      
      <table>
        <caption>Bordered table</caption>
        <thead>
          <tr>
            <th scope="col">Document title</th>
            <th scope="col">Description</th>
            <th scope="col">Year</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th scope="row">Declaration of Independence</th>
            <td>Statement adopted by the Continental Congress...</td>
            <td>1776</td>
          </tr>
        </tbody>
      </table>
    </div>