Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

State Legislature/Bill Tracker

Using editorial expertise and the Sunlight Lab's Open States API, this application creates a curated view of what is going on in a state's legislature session.

It combines data from Open States and editorial data collected with Google Docs. You can see some examples at

Examples out in the wild

Installation and configuration

This is a frontend application. It is meant to be used as a library; it is not suggested that you fork this repository unless you need to fix a bug or alter the code. It is recommended that you install the code with Bower:

bower install legislature-tracker

Include the relevant .js and .css files found in the dist folder in your HTML page. The .libs.js is included for convenience but you can include the dependencies from the bower folder as well. The application also supports module loading with RequireJS, AMD, or Browserify.

Initialize the tracker with the following:

var app = new LT({
  el: '.container-for-leg-tracker',
  OSKey: 'open-states-key-here',
  eKey: 'google-spreadsheet-key-here',
  state: 'MN',
  session: '2013-2014',
  // more options see below ...


When creating a new Legislature Tracker object, you can set the following options. All default options are in js/app.js.

Required options

The following are required for the application to work correctly

  • el: The element selector that will hold the application. This can be a CSS selector or jQuery object.
  • state: The two character state code as used by Open States.
  • session: The session key as used on Open States, like '2013-2014'.
  • OSKey: Your Open States API Key. You can get one at
  • eKey: Your Google Spreadsheet identifier. Be sure to publish your spreadsheet (File -> Publish to the web) to make it available via the Google Spreadsheet API.

Common options

The following are common options you may want to override.

  • conferenceBill: This should be true or false whether to enable the handling of conference bills. A conference bill is a third bill (other than the primary or companion) that is used often when the two bills are diverging significantly.
  • recentImage: The name of the image file to use for the recent category. Make blank to not have an image for the recent category.
  • recentChangeThreshold: The number of days to determine if a bill will be put in the recent category. The default is 7 days.
  • imagePath: The place to find images. This path is simply prepended to images and should have a trailing slash. For instance '', or './images/'. To customize images, the ideal is to copy the images found in css/images/ to your new directory and add or replace images as needed.

Hook options

These are functions that are called during processing to allow for you to override data and other functionality. Do note that if you alter certain data, you may break things.

osBillParse: A function that is called when parsing open states bill data. It passes the data at the pointer after it has been fetched from Open States and after the model parsing, but before it is loaded into the model. For, instance, this adds text names to sources:

osBillParse: function(billData, app) {
  billData.sources =, function(s, si) {
    s.text = 'SOURCE [' + si + ']';
    return s;
  return billData;

Label translations

To override the naming of certain things, like labels, you can update the the translations config object. The wordTranslations is just another options; the following are the defaults:

wordTranslations: {
  chamber: {
    'upper': 'Senate',
    'lower': 'House'
  partyAbbr: {
    'Democratic-Farmer-Labor': 'DFL',
    'Democratic': 'D',
    'Republican': 'R'
  sponsors: {
    'Primary sponsors': 'Primary sponsors',
    'primary sponsors': 'primary sponsors',
    'Primary sponsor': 'Primary sponsor',
    'primary sponsor': 'primary sponsor',
    'Co-sponsors': 'Co-sponsors',
    'co-sponsors': 'co-sponsors',
    'Co-sponsor': 'Co-sponsor',
    'co-sponsor': 'co-sponsor'

Advanced options

These options are set the same as basic options, but their default setting will work fine for most users.

  • chamberLabel: When false, the default, the label for the primary and companion bills will be Primary and Companion, respectively. When set to true the labels will be based on the bill's chambers.
  • legImageProxy: If you want to proxy images from Open States, use an URL prefix, like ''. For instance MinnPost made this custom proxy.
  • maxBills: By default, 30; the maximum number of bills that will be loaded from your Google Spreadsheet. Since each bill requires a call to OpenStates, your app may become slow if you raise this (especially on slow connections and/or older browsers).
  • tabletopOptions: An object to override any of the Tabletop.js options.
  • fieldTranslations: An object that translates the field names coming in from Tabletop into the field names that the Legislature Tracker expects them as. See js/app.js for defaults.
  • aggregateURL: An API JSON feed to get some aggregate bill counts. This is specific to MinnPost (MN) and is NOT fully supported at the moment.
  • billNumberFormat: A regex for detecting if a bill number is valid for your state. The default, /[A-Z]+ [1-9][0-9]*/ works well for most states, matching bill numbers like H 1234, S 1234 or SB 1234.
  • substituteMatch: Some legislatures will substitute the companion bill, meaning it gets dropped and the primary bill is only used. This option sets the regular expression to match actions to determine if it substituted. Define as false to turn off completely.
  • detectCompanionBill: A function or a regular expression to parse OpenStates' companion bill IDs into a bill number for automatically pairing bills with their companions in the other chamber. For regex, it will use the first match group. The default regex, /([A-Z]+ [1-9][0-9]*)$/, will find valid bills at the end of the string. If false, Legislature Tracker will not attempt to find companion bills.
  • stickMenu: True or false whether the top menu will stick to the window when scrolling. Default is true.
  • scollFocus: True or false whether the window will scroll to the top of the application when a new page is loaded. Default is true.
  • scollFocusOffset: The number of pixels to offset when scroll focus is called; a negative number is more towards the top of the page. Default is -15.
  • scollFocusTime: The amount in milliseconds that animation to scroll focus takes. Default is 500.

Google spreadsheets setup

The name and letter case of the columns and worksheets are important. See this spreadsheet for an example. There are options to change the column name mapping, but this is not well supported yet.

First make sure you have 3 sheets with the following columns:

  • Categories
    • category_id: the identifier that will be used in URL linking. Should be something like social_issues.
    • title: The text title.
    • short_title: Used for the top menu list. If none is given, the first word from the category title will be used.
    • description: The full description. Feel free to use HTML here, but do note that the content will be wrapped in a paragragh tag.
    • links: Links field, see below.
    • image: Name of image for the category. By default, these pull from the images directory, which is configurable in the imagePath option. Or if you use a full URL, starting with http it will use that directly.
  • Bills
    • bill: The primary bill name, like SF 789. This should be formatted like A 1234 with a space between the letter/chamber-appreviation and the number which should have no leading zeros.
    • companion_bill
    • conference_bill
    • categories: Category IDs separated by commas.
    • title
    • description: Descriptions get split up when in the category list view and have a "more details" link. By default, this is based on the number of words. To handle longer texts with HTML, you can use <!-- break --> to define that break point. Also note that is no description is given, then the application will use the primary bill summary which may or may not be useful and significant.
    • links: Links field, see below.
  • Events: Events are custom events like the stalling of a bill or committee that would otherwise not show up in the data from Open States. This shows up as part of the overall editorial bill information below the description.
    • bill: The primary bill ID.
    • date: A date in a format like 'YYYY-MM-DD'
    • chamber: This should be upper or lower.
    • description: A short description for the event. Feel free to use HTML here for things like links.

Link field formatting

There are a few fields that are a list of links. You should use this format so that they are parsed correctly. Do note that the parser is pretty rudimentary so don't expect much.

"Link text title|", "Another link text title|"

Changing the look

All styling is provided with CSS. The CSS with the application is a bit specific, so be sure to know how CSS specificity works. If you must, you can override the output as well.

Overriding templates

You can override the HTML templates that are used in the application and change wording as well as any HTML output. Templates are using Ractive. You can see the current templates in the js/templates/ directory. The templates are managed in the LT.LT.templates object. It is suggested to copy the template and then make alterations from there. For instance:

LT.templates['template-ebill'] = 'Your template content here';

How does your legislature work?

The Open States data is very good structured data about bills, but it is basic data that does not account for the subtleties of how legislatures work.

The Legislature tracker tries to take these subtleties into account, but may not be good enough for your legislature. Please open an issue in the queue to discuss how to address other use cases. This is currently handling the following:

  • Companion bills can be manually designated, or if not provided, the system will try to read the Open States companion bill that is designated.
  • When both primary and companion bills pass, but there are difference to reconcile, there is often a conference bill.
  • Sometimes a companion bill will get substituted, meaning it gets dropped and the primary bill is only used.
  • Some legislatures will not assign a bill right away even though it is known that it is being discussed. If there is no primary bill provided, a bill will be "stubbed" out in the interface.



All commands are assumed to be on the command line, often called the Terminal, unless otherwise noted. The following will install technologies needed for the other steps and will only needed to be run once on your computer so there is a good chance you already have these technologies on your computer.

  1. Install Git.
    • On a Mac, install Homebrew, then do: brew install git
  2. Install NodeJS.
    • On a Mac, do: brew install node
  3. Install Grunt: npm install -g grunt-cli
  4. Install Bower: npm install -g bower
  5. Install Ruby, though it is probably already installed on your system.
  6. Install Bundler: gem install bundler
  7. Install Sass: gem install sass
    • On a Mac do: sudo gem install sass
  8. Install Compass: gem install compass
    • On a Mac do: sudo gem install compass

Get code and install packages

Get the code for this project and install the necessary dependency libraries and packages.

  1. Check out this code with Git: git clone
  2. Go into the code directory: cd legislature-tracker
  3. Install NodeJS packages: npm install
  4. Install Bower components: bower install


  1. Run: grunt server
    • This will run a local webserver for development and you can view the application in your web browser at http://localhost:8136.
    • Utilize examples/example.html for development, while examples/example-dev.html is used for the deployed version, and index-build.html is used to test the build before deployment.
    • The server runs grunt watch which will watch for linting JS files and compiling SASS. If you have your own webserver, feel free to use that with just this command.


Built versions will only be regularly committed for tagged releases.

  1. Uses grunt which depends on Node. To install: npm install -g grunt-cli && npm install
  2. (for specific version) Update version in: package.json
  3. Run: grunt
  4. (for specific version) Tag release with appropriate version: git tag 0.1.1


The basic idea of the application is pulling together editorial knowledge about bills and combining it with Open States data about the bills to create a focused and useful interface to keep track of the important activities of a legislature session.

OS prefixes refer to Open States data, while E or e prefixes refer to editorial data and objects.

Each editorial (or meta) bill refers to one or more Open States (or actual) bill.

Cross-browser compatibility

The goal of this project is to be compatible with all major modern browsers including IE8.


To ensure that both memory and network usage is minimized, there is some basic caching happening.

For model instances, we wrap the creation of models in the following method:

LT.getModel('ModelName', 'identifying_attribute', attributes)

For fetching models we wrap fetchings so that it only happens once:

// Returns a jQuery promise object

Google Spreadsheets

This application uses Tabletop.js to read in data from Google Spreadsheets. Due to the fact that Google does not guarantee up time or ability to handle requests, it is good practice to cache the outputs for production. Tabletop has some recent additions to handle proxy via saving the outputs to a place like S3, as well as more traditional proxy like gs-proxy. Use the tabletopOptions option to set any of the tabletop options.


  • Some icons provided by The Noun Project:
    • Congress by Martha Ormiston; Energy by NDSTR
    • GayMarriage by MaurizioFusillo
    • Education by Thibault Geffroy
    • Time by Richard de Vos
    • Capital by Jonathan Keating
    • Paper by Tom Schott
    • Bank by Ilaria Baggio
    • Group by Alexandra Coscovelnita
    • Check mark by Spencer Cohen
    • Back by John Chapman.

About Us

MinnData, the MinnPost data team, is Alan, Tom, and Kaeti and all the awesome contributors to open source projects we utilize. See our work at

                                                   `.  \
                                                     \  \
                                                      .  \
                                                      :   .
                                                      |    .
                                                      |    :
                                                      |    |
      ..._  ___                                       |    |
     `."".`''''""--..___                              |    |
     ,-\  \             ""-...__         _____________/    |
     / ` " '                    `""""""""                  .
     \                                                      L
     (>                                                      \
    /                                                         \
    \_    ___..---.                                            L
      `--'         '.                                           \
                     .                                           \_
                    _/`.                                           `.._
                 .'     -.                                             `.
                /     __.-Y     /''''''-...___,...--------.._            |
               /   _."    |    /                ' .      \   '---..._    |
              /   /      /    /                _,. '    ,/           |   |
              \_,'     _.'   /              /''     _,-'            _|   |
                      '     /               `-----''               /     |
                      `...-'                                       `...-'


An application that pulls in Open States data and combines with editorial expertise to create a live view into a state legislature session.







No packages published