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


This news app is built on our interactive template. Check the readme for that template for more details about the structure and mechanics of the app, as well as how to start your own project.

Getting started

To run this project you will need:

  • Node installed (preferably with NVM or another version manager)
  • The Grunt CLI (install globally with npm i -g grunt-cli)
  • Git

With those installed, you can then set the project up using your terminal:

  1. Pull the code - git clone
  2. Enter the project folder - cd elections20-interactive
  3. Install dependencies from NPM - npm install
  4. Start the server - grunt

Running tasks

Like all interactive-template projects, this application uses the Grunt task runner to handle various build steps and deployment processes. To see all tasks available, run grunt --help. grunt by itself will run the "default" task, which processes data and starts the development server. However, you can also specify a list of steps as arguments to Grunt, and it will run those in sequence. For example, you can just update the JavaScript and CSS assets in the build folder by using grunt bundle less.

Common tasks that you may want to run include:

  • sheets - updates local data from Google Sheets
  • docs - updates local data from Google Docs
  • google-auth - authenticates your account against Google for private files
  • static - rebuilds files but doesn't start the dev server
  • cron - runs builds and deploys on a timer (see tasks/cron.js for details)
  • publish - uploads files to the staging S3 bucket
    • publish:live uploads to production
    • publish:simulated does a dry run of uploaded files and their compressed sizes

Tracked events

  • route - sends the URL fragment as the event label
  • county-metric - the county table's custom metric was updated
  • county-sort - the user clicked a header to re-sort a county table
  • clicked-bubble - the user clicked a bubble on the margin plot
  • clicked-cartogram - the user clicked a state on the cartogram
  • clicked-map - the user clicked a state on the national map
  • tab-selected - the user manually chose a tab to view

Additional links and params

  • Homepage embed: /homepage.html
    • display=margins,cartogram,map controls which viz displays on load
  • Balance of Power embed (House and Senate bars): /embedBOP.html
    • president=true adds electoral totals to the top (for use on homepage)
    • hideCongress=true hides House and Senate bars on mobile view
    • onlyPresident=true hides House and Senate bars on all views
    • inline=true for side-by-side display (for use on liveblog)
    • theme=dark for dark theme
  • Internal ballot initiative board: /#/ballots
  • Results embed customizer: /customizer.html
  • Share pages, with metadata for social cards
    • /share/XX.html - state pages, where XX is the postal code
    • /share/president.html - Presidential big board
    • /share/senate.html - Senate big board
    • /share/house.html - House big board
    • /share/governor.html - Governors big board


Fatal error: Port 35729 is already in use by another process.

The live reload port is shared between this and other applications. If you're running another interactive-template project or Dailygraphics Next, they may collide. If that's the case, use --reload-port=XXXXX to set a different port for the live reload server. You can also specify a port for the webserver with --port=XXXX, although the app will automatically find the first available port after 8000 for you.