Inspiration
The idea for PolicyPulse came from a pretty simple problem: environmental policies are easy to announce and surprisingly hard to understand.
You might hear that your city is introducing a bag fee, a composting program, or a recycling incentive. Then you open the actual policy page and get hit with legal wording, dates, exceptions, percentages, and rules. Even after reading all of that, you're still left with the question we cared about most:
What does this actually mean for my household?
We wanted to build something that answered that question without pretending to know more than it does.
That last part became important very quickly. It would have been easy to make a dashboard with impressive-looking numbers about emissions or environmental impact. But if we couldn't explain where a number came from, we didn't want to show it.
So PolicyPulse became less about "predicting the future" and more about taking a policy, combining it with a household's situation, and showing the math clearly enough that someone can decide what to do with it.
What it does
PolicyPulse takes environmental policies and makes them easier to understand on a household level.
A user can look through policies, read a plain-language explanation, enter some basic information about their household, and then simulate how a policy could affect them.
The simulator is the part we spent the most time on.
Instead of giving you one mysterious number, it shows the assumptions behind the result. You can change things like how often you shop or how often you use reusable bags and immediately see the estimate change.
For a bag fee, for example, part of the calculation is basically:
$$ \text{monthly trips} = \text{weekly trips} \times \frac{52}{12} $$
Then we estimate how many disposable bags are still being used and multiply that by the policy's fee.
Nothing groundbreaking mathematically. That's kind of the point. The value is that you can actually see what is happening instead of getting a number from a black box.
PolicyPulse currently includes scenarios for bag fees, composting, and recycling incentives.
We also built a Policy Explorer, household profiles, saved policies, checklists, private notes, community-style household comparisons, and a printable impact summary.
The app has a demo mode as well. The demo city and its policies are fictional, and we make that obvious in the UI instead of quietly presenting fake data as real policy.
How we built it
We built PolicyPulse with Next.js, React, TypeScript, Tailwind CSS, Recharts, Zod, and Vitest.
One decision that saved us a lot of trouble was keeping the calculation code separate from the UI.
The simulator doesn't calculate things inside React components. Each scenario has its own TypeScript functions, and the same functions are used everywhere the result appears.
That means the dashboard, simulator, comparison page, and printable summary aren't each doing their own version of the math.
We also separated policy data from the rest of the application. PolicyPulse can run using the built-in demo data, a curated source-linked record, or data from an external provider that matches the expected schema.
For household data, we kept things simple. Profiles, bookmarks, simulator settings, notes, and checklist progress are stored in the browser with localStorage.
No account is required.
One thing we cared about more than we expected was keeping different kinds of numbers separate. A value coming directly from a policy is not the same thing as a household input, and neither of those is the same thing as a hypothetical value someone changes in the simulator.
So we track where assumptions come from and show that in the interface.
If you change a policy's fee to test a hypothetical scenario, for example, the actual policy value doesn't suddenly disappear. You can still see what is real and what you changed.
Challenges we ran into
The hardest part wasn't really building the pages. It was deciding what we were allowed to claim.
Environmental impact gets complicated fast.
For example, if our composting model tells us that a household could divert a certain number of pounds of food waste, it is tempting to convert that into an emissions number.
We didn't.
We didn't have a reliable conversion that made sense for every situation, so pounds stay pounds. The same rule applies throughout the project. If we don't have enough information to make a calculation honestly, we leave it out.
We also had to think a lot about what "baseline" actually means.
At one point, a bag-fee calculation could make it look like a household was saving money when it had actually just started paying a new fee. The arithmetic itself wasn't complicated. The comparison was wrong.
That bug was a good reminder that a formula can be technically valid and still answer the wrong question.
Another annoying problem was localStorage.
We wanted household settings to survive a refresh, but Next.js renders on the server first, where browser storage doesn't exist. Reading saved state at the wrong time can cause hydration problems between the server-rendered page and what React sees in the browser.
We ended up loading saved state only after the client is ready and keeping the initial render deterministic.
Accessibility also caught us a few times. Things that looked completely fine visually still had issues with heading structure, labels, keyboard navigation, or contrast. Testing those parts forced us to fix things we probably wouldn't have noticed just by clicking around the app ourselves.
And then there was the data problem: validating JSON is not the same as validating truth.
Zod can tell us that a policy has the fields we expect. It cannot tell us whether a law is still current or whether somebody copied a fee incorrectly. Real policy data still needs real sources and human review.
Accomplishments that we're proud of
I'm proud that the simulator doesn't hide its work.
It's really easy to make a clean dashboard that gives someone a number and asks them to trust it. We went the other direction. PolicyPulse shows the assumptions, the formula, and where the inputs came from.
I'm also happy with how much of the app works without needing an account or backend. You can create a household profile, change simulations, save policies, write notes, and work through checklists while keeping that information on your own device.
Another thing that sounds small but took real effort was making the demo honest.
If a location isn't supported, we say it isn't supported. We don't quietly swap in the fictional city.
If something is example data, it says so.
If a simulator value is hypothetical, it says so.
That made the project feel a lot more trustworthy than if we had tried to make the demo look bigger than it really was.
We also ended up building a proper test suite around the calculations and edge cases instead of only checking the happy path manually.
What we learned
The biggest thing we learned is that explaining a calculation is sometimes harder than writing it.
The formulas in PolicyPulse aren't advanced mathematics. The difficult part is figuring out which values belong in the formula, where those values came from, what the result actually means, and where the model stops being reliable.
We also learned that data provenance can't just be a disclaimer at the bottom of a page.
It has to be part of the data model.
Once we started distinguishing between policy values, household inputs, illustrative defaults, and what-if settings in the code itself, a lot of UI decisions became much easier.
On the engineering side, separating the simulation logic from React was probably one of our best decisions. It made the same calculations reusable across the entire application and made testing much simpler.
Testing also changed the way we looked at the project.
We originally thought of tests mostly as a way to check whether something worked. By the end, they were also a way to challenge our assumptions. A test that proves your code follows the wrong definition isn't very useful.
That sounds obvious now.
It wasn't always obvious while we were building it.
What's next for policy-pulse
The biggest next step is adding more real policy coverage.
The architecture already supports replacing the demo data with validated policy records, but collecting and maintaining trustworthy policy information is a much bigger job than just plugging in an API.
We'd like to expand beyond waste and recycling too. Water, household energy, and transportation policies could all fit the same general idea, as long as we can build models that are understandable and based on defensible data.
There are also smaller things we want to improve: better source freshness checks, more jurisdictions, better comparison tools, and eventually optional accounts for people who actually want their data synced between devices.
But we don't want to lose the thing that made the project interesting to us in the first place.
A person should be able to open a policy, understand what changed, see how the numbers apply to their situation, and see exactly how the estimate was calculated.
No twenty-page document required.
Built With
- autoprefixer
- clsx
- css3
- eslint
- git
- github-actions
- html5
- javascript
- json
- localstorage
- lucide-react
- netlify
- next.js
- node.js
- npm
- postcss
- react
- react-dom
- recharts
- tailwind-css
- tailwind-merge
- typescript
- vite
- vitest
- zod
Log in or sign up for Devpost to join the conversation.