diff --git a/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx b/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx
index 09d22b69c..b66668317 100644
--- a/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx
+++ b/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx
@@ -18,12 +18,13 @@ import SnackNotifier from '../../common/components/Widgets/SnackNotifier';
import ElectionStore from '../../stores/ElectionStore';
import CopyChip from './CopyChip';
import copyAndToast from './copyAndToast';
+import formatDateLong from './dateHelpers';
import ElectionFinderHeader from './ElectionFinderHeader';
import RowKebabMenu from './RowKebabMenu';
import {
ActionDivider, DarkTooltip,
CandidateActions as CandidateActionsRow, CandidateInfo, CandidateList, CandidateName,
- CandidateParty, CandidateRow, DetailTitle, ElectionTitleRow,
+ CandidateParty, CandidateRow, DetailTitle, ElectionDetailDate, ElectionTitleRow,
ExpandCollapseButton, ExpandCollapseRow, ExpandMoreIcon,
HighlightSpan, InlineSearchField, NoResults,
OfficeHeader, OfficeHeaderActions, OfficeHeaderLeft, OfficeName, OfficePrimaryPartySpan,
@@ -70,6 +71,7 @@ function ElectionFinderForElection () {
// Derived from stores — no need to cache in state
const electionName = ElectionStore.getElectionName(googleCivicElectionId) || 'Election';
+ const electionDayText = ElectionStore.getElectionDayText(googleCivicElectionId);
const isUpcoming = ElectionStore.isElectionUpcoming(googleCivicElectionId);
const electionList = ElectionStore.getElectionList();
const stateElections = electionList.filter(
@@ -197,6 +199,7 @@ function ElectionFinderForElection () {
const totalResults = electionSearchText ?
filteredOffices.reduce((sum, o) => sum + (o.candidate_list || []).length, 0) :
null;
+ const officeCount = ballotItems.length;
return (
<>
@@ -205,14 +208,19 @@ function ElectionFinderForElection () {
- {electionName}
+
+ {electionName}
+ {' '}
+ {`(${officeCount})`}
+
{nextReleaseFeaturesEnabled && (
@@ -258,6 +266,10 @@ function ElectionFinderForElection () {
)}
+ {electionDayText && (
+ {formatDateLong(electionDayText)}
+ )}
+
{filteredOffices.length > 0 && (
@@ -304,7 +316,11 @@ function ElectionFinderForElection () {
)}
{filteredOffices.length === 0 && (
- {ballotLoaded ? 'No results found.' : 'Loading...'}
+
+ {!ballotLoaded && 'Loading...'}
+ {ballotLoaded && electionSearchText && 'No results found.'}
+ {ballotLoaded && !electionSearchText && 'Our team hasn’t assembled the data for this election yet. We usually have election data 45 days before each election.'}
+
)}
>
diff --git a/src/js/pages/ElectionFinder/ElectionFinderForState.jsx b/src/js/pages/ElectionFinder/ElectionFinderForState.jsx
index 1b3beab37..a2fcd26c5 100644
--- a/src/js/pages/ElectionFinder/ElectionFinderForState.jsx
+++ b/src/js/pages/ElectionFinder/ElectionFinderForState.jsx
@@ -13,6 +13,7 @@ import SnackNotifier from '../../common/components/Widgets/SnackNotifier';
import ElectionStore from '../../stores/ElectionStore';
import CopyChip from './CopyChip';
import copyAndToast from './copyAndToast';
+import formatDateLong from './dateHelpers';
import ElectionFinderHeader from './ElectionFinderHeader';
import RowKebabMenu from './RowKebabMenu';
import {
@@ -30,14 +31,6 @@ const SORTED_STATES = Object.entries(stateCodeMap)
.filter(([code]) => code !== 'NA')
.sort((a, b) => a[1].localeCompare(b[1]));
-const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
-
-function formatDateLong (dateString) {
- if (!dateString) return '';
- const [y, m, d] = dateString.split('-');
- return `${MONTH_ABBR[parseInt(m, 10) - 1]} ${parseInt(d, 10)}, ${y}`;
-}
-
function sortByDateAsc (a, b) {
return (a.election_day_text || '').localeCompare(b.election_day_text || '');
}
diff --git a/src/js/pages/ElectionFinder/ElectionFinderHeader.jsx b/src/js/pages/ElectionFinder/ElectionFinderHeader.jsx
index 2996e747c..389804964 100644
--- a/src/js/pages/ElectionFinder/ElectionFinderHeader.jsx
+++ b/src/js/pages/ElectionFinder/ElectionFinderHeader.jsx
@@ -1,12 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { ElectionNameH1, ElectionStateLabel } from '../../components/Style/BallotTitleHeaderStyles';
-import { Breadcrumb, BreadcrumbAnchor } from './electionFinderStyles';
+import { ElectionNameH1 } from '../../components/Style/BallotTitleHeaderStyles';
+import { Breadcrumb, BreadcrumbAnchor, ElectionFinderStateLabel } from './electionFinderStyles';
function ElectionFinderHeader ({ breadcrumbs, stateLabel, subtitle }) {
return (
<>
- {stateLabel && {stateLabel}}
Election Finder
{breadcrumbs && breadcrumbs.length > 0 && (
@@ -28,6 +27,7 @@ function ElectionFinderHeader ({ breadcrumbs, stateLabel, subtitle }) {
)}
{subtitle && {subtitle}}
+ {stateLabel && {stateLabel}}
>
);
}
diff --git a/src/js/pages/ElectionFinder/ElectionFinderHome.jsx b/src/js/pages/ElectionFinder/ElectionFinderHome.jsx
index ba0ae843b..ce0256923 100644
--- a/src/js/pages/ElectionFinder/ElectionFinderHome.jsx
+++ b/src/js/pages/ElectionFinder/ElectionFinderHome.jsx
@@ -12,6 +12,7 @@ import SnackNotifier from '../../common/components/Widgets/SnackNotifier';
import ElectionStore from '../../stores/ElectionStore';
import CopyChip from './CopyChip';
import copyAndToast from './copyAndToast';
+import formatDateLong from './dateHelpers';
import ElectionFinderHeader from './ElectionFinderHeader';
import RowKebabMenu from './RowKebabMenu';
import {
@@ -25,14 +26,6 @@ import webAppConfig from '../../config';
const nextReleaseFeaturesEnabled = webAppConfig.ENABLE_NEXT_RELEASE_FEATURES === undefined ? false : webAppConfig.ENABLE_NEXT_RELEASE_FEATURES;
-const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
-
-function formatDateLong (dateString) {
- if (!dateString) return '';
- const [y, m, d] = dateString.split('-');
- return `${MONTH_ABBR[parseInt(m, 10) - 1]} ${parseInt(d, 10)}, ${y}`;
-}
-
function sortByDateAsc (a, b) {
return (a.election_day_text || '').localeCompare(b.election_day_text || '');
}
diff --git a/src/js/pages/ElectionFinder/dateHelpers.js b/src/js/pages/ElectionFinder/dateHelpers.js
new file mode 100644
index 000000000..4bc8c3cba
--- /dev/null
+++ b/src/js/pages/ElectionFinder/dateHelpers.js
@@ -0,0 +1,8 @@
+const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+// "2026-03-17" -> "Mar 17, 2026"
+export default function formatDateLong (dateString) {
+ if (!dateString) return '';
+ const [y, m, d] = dateString.split('-');
+ return `${MONTH_ABBR[parseInt(m, 10) - 1]} ${parseInt(d, 10)}, ${y}`;
+}
diff --git a/src/js/pages/ElectionFinder/electionFinderStyles.js b/src/js/pages/ElectionFinder/electionFinderStyles.js
index 514eaeeb0..0e406c3cc 100644
--- a/src/js/pages/ElectionFinder/electionFinderStyles.js
+++ b/src/js/pages/ElectionFinder/electionFinderStyles.js
@@ -2,6 +2,8 @@ import { TextField, Tooltip, tooltipClasses } from '@mui/material';
import React from 'react'; // eslint-disable-line no-unused-vars
import { Link } from 'react-router-dom';
import styled from 'styled-components';
+import colors from '../../common/components/Style/Colors';
+import { ElectionStateLabel } from '../../components/Style/BallotTitleHeaderStyles';
export const ActionChip = styled('button')`
display: inline-flex;
@@ -125,6 +127,23 @@ export const ElectionDateText = styled('span')`
white-space: nowrap;
`;
+// Date shown directly under the election title on the ForElection page.
+// Color matches ElectionStateLabel so the state line + date frame the title in the same hue.
+export const ElectionDetailDate = styled('div')`
+ color: ${colors.middleGrey};
+ font-size: 16px;
+ font-weight: 400;
+ margin-top: 0;
+ margin-bottom: 16px;
+ white-space: nowrap;
+`;
+
+// Slightly larger state label used in the Election Finder header.
+// Scoped so the shared ElectionStateLabel stays unchanged on Ballot pages.
+export const ElectionFinderStateLabel = styled(ElectionStateLabel)`
+ font-size: 15px;
+`;
+
export const ElectionLink = styled('span')`
font-size: 17px;
color: #206bc4;
@@ -176,7 +195,8 @@ export const ElectionTitleRow = styled('div')`
display: flex;
align-items: center;
gap: 8px;
- margin-bottom: 8px;
+ margin-top: 4px;
+ margin-bottom: 0;
`;
export const ExpandCollapseButton = styled('button')`