Welcome to the CALC Style Guide!

CALC's content is structured using 18F's standard voice, which is described in the 18F Content Guide.

CALC uses Bourbon and Neat as the foundation for its styles, with alterations based on the U.S. Web Design Standards (USWDS).

While not all parts of the site adhere to a single unifying philosophy, we try to build new parts in accordance with Aaron Gustafson's three maxims for progressive enhancement with JavaScript:

  1. Make sure all content is accessible and all necessary tasks can be completed without JavaScript turned on.
  2. Use JavaScript to generate any additional markup it needs.
  3. Apply no style before its time.


Most typography-related styling is defined in base/_typography.scss and base/_variables.scss.


Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

Paragraph body text go paragraph body text go. Paragraph body text go. Paragraph body text go paragraph body text go. Paragraph body text go. Paragraph body text go paragraph body text go. Paragraph body text go. Paragraph body text go paragraph body text go. Paragraph body text go.Paragraph body text go paragraph body text go. Paragraph body text go.

  • An unordered list
  • Organizing many things
  • Order from chaos
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<h3>Heading 3</h3>
<h4>Heading 4</h4>
<h5>Heading 5</h5>
<h6>Heading 6</h6>
<p>Paragraph body text go paragraph body text go. Paragraph body text go. Paragraph body text go paragraph body text go. Paragraph body text go. Paragraph body text go paragraph body text go. Paragraph body text go. Paragraph body text go paragraph body text go. Paragraph body text go.Paragraph body text go paragraph body text go. Paragraph body text go.</p>
  <li>An unordered list</li>
  <li>Organizing many things</li>
  <li>Order from chaos</li>

Ajax Form + Upload Widget

Our upload widget is implemented using the techniques outlined in Styling & Customizing File Inputs the Smart Way and Drag and Drop File Uploading by Osvaldas Valutis.

We use an <upload-widget> with a nested <input is="upload-input"> to progressively enhance the front end. However, using them is rarely needed in practice, as frontend.upload.UploadWidget contains the Django widget for rendering all the required HTML.

Form Submission

Due to the technical limitations of HTML5, forms containing the progressively-enhanced upload widget must be submitted via ajax. This can be accomplished via a custom <form is="ajax-form"> web component.

frontend.ajaxform contains utilities for processing forms submitted by this web component in a progressively-enhanced way.

For a simple ajax form code example that also embeds an upload widget, see styleguide/ and styleguide/templates/styleguide_ajaxform_example.html.


Some radios
CSV only, please.


Any response from the ajax form that dynamically updates content on the page instead of redirecting the user to a new page should contain an <alerts-widget> which wraps a message to inform users about what just happened. This will automatically be focused when the content is injected into the page, allowing screen readers to announce it.

To experience this in action, try submitting invalid data into the example form above.

Graceful Degradation

Note that the upload widget gracefully degrades to a standard HTML file input if either JS is disabled or any required HTML5 features are unavailable:

Example (no JavaScript)

XLS, XLSX, or CSV format, please.

Existing Filenames

In some cases, it may be preferable to indicate to a user that a file upload field is not only optional, but that a default which the user has uploaded earlier will be used if nothing else is provided.


The baseline experience simply explains to the user that leaving the field blank will result in the use of their previously-uploaded file.

Example (no JavaScript)

You've already uploaded boop.csv. You can keep using it or select a new file to replace it.
XLS, XLSX, or CSV format, please.

The server, of course, will not receive any data if the user doesn't supply a file. Finding and using the previously uploaded file is the server's responsibility.

However, if proper browser support exists, the user experience is progressively enhanced: the widget will appear populated, but as with the baseline experience, the server will not actually receive any data unless the user explicitly chooses a different file.


You've already uploaded boop.csv. You can keep using it or select a new file to replace it.
XLS, XLSX, or CSV format, please.


Use primary buttons to indicate primary actions, default to indicate less important actions, and secondary to indicate actions such as cancelling a process or deleting information.

Styling is defined in components/_buttons.scss.


<button>Default button</button>
<a class="button button-primary" href="#">Link</a>
<button class="button-primary">Button</button>
<input class="button-primary" type="button" value="Input">
<input class="button-primary" type="submit" value="Submit">
<button class="button-disabled">Disabled button</button>
<button class="button-secondary">Secondary button</button>
<button class="button-gray">Gray button</button>
<button class="button-link">Link button</button>

Steps Widget

The steps widget can be used to visualize the user's progress through a multi-step process.

Users assisted by screen readers will hear an audio description of the visualization, e.g. "Step 2 of 3", followed by the name of the current step.

Rather than writing out the raw HTML of the steps widget, we recommend using frontend.steps.StepsWidget.


The markup used by this widget is defined in frontend/steps.html, while styling is in components/_steps.scss.


These are similar to the US Web Design Standards, but add a stroke and the CALC standard border radius.

Styling is defined in components/_alerts.scss.


<div class="alert alert-error" role="alert">
  <p>Better get out your eraser.</p>


<div class="alert alert-success" role="alert">
  <h3>Success alert</h3>


<div class="alert alert-warning" role="alert">
  <h3>Warning alert</h3>
  <p>Danger, danger, Will Robinson!</p>


<div class="alert alert-info" role="alert">
  <h3>Info alert</h3>
  <p>Now you have The Information!</p>


Defined in components/_tables.scss.

When displaying data in tables, be sure to add the number class to cells that contain numbers. This will monospace and right-align those columns. For dollar amounts, you'll need to also add the currency class to get the dollar sign to appear. You can add a highlight-on-hover effect to a table's rows by including the hoverable class on the table.


SIN Service proposed Minimum education Minimum years of experience Price offered to GSA (including IFF)
132-51 Project Manager Bachelors 5 115.99
132-17 Software Programmer, Associate Bachelors 0 37.59
132-17 Software Programmer I Bachelors 3 41.59
132-17 Software Programmer II Bachelors 5 52.03
132-17 Software Programmer III Bachelors 7 65.72
<table class="hoverable">
      <th>Service proposed</th>
      <th>Minimum education</th>
      <th class="number">Minimum years of experience</th>
      <th class="number">Price offered to GSA (including IFF)</th>
      <td>Project Manager</td>
      <td class="number">5</td>
      <td class="number currency">115.99</td>
      <td>Software Programmer, Associate</td>
      <td class="number">0</td>
      <td class="number currency">37.59</td>
      <td>Software Programmer I</td>
      <td class="number">3</td>
      <td class="number currency">41.59</td>
      <td>Software Programmer II</td>
      <td class="number">5</td>
      <td class="number currency">52.03</td>
      <td>Software Programmer III</td>
      <td class="number">7</td>
      <td class="number currency">65.72</td>

When errors are displayed in tables, they look like this. Note that this markup is automatically generated on Step 3.


Review submitted price list data
SIN(s) proposed Service proposed (e.g. job title/task) Min. education (or certification level) Min. years of experience Unit of issue Price offered to GSA (including IFF)
none Button Presser NA 1 Hour $9.00
none Button Presser NA all of them Hour $9.00

Excel Tables

Defined in components/_exceltables.scss.

Sometimes we need to display "screenshots" of Microsoft Excel tables to users, to communicate what kind of spreadsheet they need to upload. Doing this with standard HTML tables that are styled to look like excel tables allows us to accomplish this in an accessible and easily changeable way.

The base class just styles the table to look like a standard Excel sheet without any styling:


<div class="excel-wrapper">
  <table class="excel">

But we have extra classes that we can layer on top to emulate the styling of specific contract vehicle spreadsheets.

For example, here's a style for Schedule 70 proposed price lists:


132-51 Project Manager Bachelors 5 $125 Hour All Commercial
<div class="excel-wrapper">
  <table class="excel excel-schedule-70">
      <td>SIN(s) PROPOSED</td>
      <td>SERVICE PROPOSED (e.g. Job Title/Task)</td>
      <td>UNIT OF ISSUE (e.g. Hour, Task, Sq ft)</td>
      <td>Project Manager</td>
      <td>All Commercial</td>


Defined in components/_forms.scss and uswds/_forms.scss.


Helpful error message
<label for="input-type-text">Text input label</label>
<input id="input-type-text" name="input-type-text" type="text">

<label for="input-has_focus">Text input focused</label>
<input class="input-focus" id="input-focus" name="input-focus" type="text">

<fieldset class="fieldset-error">
  <span class="input-error-message" id="input-error-message" role="alert">Helpful error message</span>
  <label class="input-error-label" for="input-error">Text input error</label>
  <input id="input-error" name="input-error" type="text" aria-describedby="input-error-message">

<label for="input-type-textarea">Text area label</label>
<textarea id="input-type-textarea" name="input-type-textarea"></textarea>


<label for="options">Dropdown label</label>
<select name="options" id="options">
  <option value="value1">Option A</option>
  <option value="value2">Option B</option>
  <option value="value3">Option C</option>

Radio Buttons and Checkboxes

Because USWDS uses a slightly different HTML structure for radio buttons and checkboxes from Django, we need to use custom widgets: frontend.widgets.UswdsRadioSelect and frontend.widgets.UswdsCheckbox.

For a simple code example, see styleguide/ and styleguide/templates/styleguide_radio_checkbox_example.html.



Dates is a custom Django form field and widget that offers three separate text fields for users to enter dates, as per the USWDS section on date input.

We use a <uswds-date> to provide some optional dynamic functionality, such as automatically advancing focus to the next input field in the date whenever /, ., or - is pressed.

For a simple code example, see styleguide/ and styleguide/templates/styleguide_date_example.html.


Date For example: 04 28 2016

Form Button Row

Defined in components/_formbuttonrow.scss.

The form-button-row widget is used to add common buttons to the bottom of forms in a multi-step process, such as the Data Capture Upload flow.


Provide details about the contract.
<div class="form-button-row clearfix">
  <a href="#" class="button button-outline button-previous">Previous</a>

  <div class="submit-group">
    <button type="submit" class="button-primary">Next</button>
    <span class="submit-label">
      Provide details about the contract.


Expandable Area

The <expandable-area> web component makes it possible to progressively enhance an area to be initially collapsed, and expandable via user interaction. The first child element is assumed to be the expander (which is dynamically given the button role) while all of its siblings are the expandable content.


Why are computers so hard to use?

Here is a paragraph of explanatory text.

Here is another paragraph of explanatory text.

  <h6>Why are computers so hard to use?</h6>
  <p>Here is a paragraph of explanatory text.</p>
  <p>Here is another paragraph of explanatory text.</p>

When JavaScript isn't available, the hidden content is always shown:

Example (no JavaScript)

Why are computers so hard to use?

Here is a paragraph of explanatory text.

Here is another paragraph of explanatory text.

Styling for this component can be found in components/_expandablearea.scss.

Defined in components/_dialogs.scss and data-capture/modal-dialogs.js.

Modal dialogs provide a way of display a dialog, such as a confirmation message, and associated actions in a visually-pleasing, styleable manner. We use the a11y-dialog library for modal dialog support.

Due to the way a11y-dialog works, markup for the modals needs to be placed outside of the page's <main> element in order to be read properly by screenreaders. In templates, place modal markup in the {% block modals %} block.


<button data-a11y-dialog-show="example-dialog">Open Dialog</button>

<!-- Note that any modal markup should be placed in {% block modals %}
otherwise screenreading will not work properly.
Screenreading will not work properly in this example for instance. -->
<div id="example-dialog" aria-hidden="true" class="dialog">
  <div class="dialog-overlay" tabindex="-1" data-a11y-dialog-hide></div>
  <div class="dialog-content" role="dialog" aria-labelledby="example-dialog-title" aria-describedby="example-dialog-description">
    <div role="document">
      <h1 id="example-dialog-title">
        This is an example dialog title

      <p id="example-dialog-description">
        Here is some content in the dialog.
        Click "Dismiss Dialog" or anywhere outside of this dialog to close me.

      <div class="dialog-buttons">
        <button class="button-primary" data-a11y-dialog-hide>
          Dismiss dialog

When JavaScript is not available, the activator element will do whatever it previously did before being modified to open a dialog.


Emails are delivered in both plain text and HTML formats. Our HTML emails are based on Lee Munroe's Really Simple Responsive HTML Email Template.

The base template used to render emails is at email/base.html, while common template tags are in the email_utils template tag library.

Examples of each email type that can be sent from CALC are listed below.

Compatibility Mode

Because we're designing the new parts of CALC to be progressively enhanced, they can actually work without JavaScript. One advantage of this approach is that in the rare case that the site doesn't work, a user can disable JavaScript in their browser and reload the page—it can potentially fix things.

However, there's a few problems with this:

  1. Most users don't know what JavaScript is, much less how to disable it.
  2. Even if a user knows what JavaScript is and how to disable it in their browser, they have no idea when a JavaScript error has actually occurred.

Fortunately, it is possible to use JavaScript to detect when a JavaScript error has occurred, so we do this on progressively enhanced CALC pages. Whenever a JS error occurs, a warning appears that allows users to opt-in to compatibility mode. If the user opts-in, the server will stop sending JS to the client, ensuring a baseline experience.

To see what this looks like in action, you can forcibly trigger a JS error on this page via the button below.