Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Tally

Liam GourLiam Gour258 downloads

Track daily habits from your notes. Reads and writes plain frontmatter and Dataview inline fields, draws a year heatmap and streaks, and is built to be tapped one-handed on a phone.

Add to Obsidian
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
Tally screenshot
  • Overview
  • Scorecard
  • Updates7

A habit tracker for Obsidian that is built to be tapped on a phone and to leave nothing behind.

Tally on a phone — the Today, Habits and Reports tabs

The one promise

Delete this plugin and every reading you made is still there, in plain markdown, still readable and still queryable.

That is not a feature, it is the constraint the whole thing is designed around:

  • Readings live in the note. Gym: 30 min in frontmatter, or Gym:: 30 min in the body. Nothing is written to a sidecar file, a database, or a JSON blob.
  • Every write goes through app.fileManager.processFrontMatter, which is Obsidian's own API — so your formatting, comments and property order survive.
  • Clearing a habit deletes the key rather than writing 0. A missing property means "no entry"; 0 means "I did zero", and those are different facts.
  • The plugin's own settings hold only the list of names you consider habits. Lose it and you lose a list, not a data point.

What it looks like

Every colour comes from your theme's own variables, so Tally follows whatever you already run. Here is the same plugin in a light theme:

The same three tabs in a light theme

Reports, in a desktop pane. Four figures for the year, a grid where every square opens that day, and a row per habit that expands into its own heatmap, month bars and run history.

The Reports tab open in a desktop pane

Logging is one tap, and a measured habit asks how much on a sheet sized for a thumb. The editor breaks a habit into four questions and shows the line it will write in your note, live, above them.

The habit editor and the amount sheet

Settings are grouped into seven sections with a navigation rail — a chip row on a phone, a sidebar on a desktop, from one DOM.

The settings tab, showing the habit list

They are also searchable across every section at once, so you do not have to know which section a setting lives in:

Searching settings for "export"

Installing and updating

Tally is not in the community store yet. Until it is, there are two ways to run it, and both keep updating.

With BRAT — the route Obsidian recommends for a plugin that has not been published yet. Install BRAT from the community store, run BRAT: Add a beta plugin for testing, and give it:

https://github.com/Virus7976/2026-09-02_school_habit-tracker_v1

BRAT installs the latest release and checks for a newer one from then on, so a new release here becomes an update in your vault.

By hand — download main.js, manifest.json and styles.css from the latest release into <your vault>/.obsidian/plugins/tally/, then enable Tally under Community plugins. Repeat to update.

Once it is approved, install it from Community plugins → Browse → Tally and Obsidian handles updates itself. A vault already running the BRAT copy should remove it there first, so the same plugin is not installed twice.

Either way your readings are untouched — they live in your notes, not in the plugin.

It follows Dataview's rules, without needing Dataview

Field names, inline fields and value typing all behave the way Dataview defines them — implemented natively, so nothing breaks when Dataview is switched off:

Dataview rule Here
Key:: value on its own line read
[Key:: value] and (Key:: value) mid-sentence read
Inside a list item or a task read
Inside a code span or a fenced block ignored, as Dataview ignores it
Canonical names — Lights out by 11 → lights-out-by-11 resolved
A bare number types as a number, 30 min stays a string same
A repeated key becomes an array same
Frontmatter wins a collision with an inline field same

If Dataview is installed and enabled, its API is used directly instead — same source, same predicates — so a tally block and a dv query cannot disagree.

It agrees with Heatmap Tracker

The data model was read out of Heatmap Tracker's shipped bundle rather than its README, and kept faithful on purpose: the date comes from the filename, days merge by summing, values coerce by parseFloat (so 30 min is 30 and yes is 1), buckets scale linearly across the range, and the default and danger palettes are byte-identical. A day painted by an existing heatmap-tracker block and the same day painted here are the same colour.

The deliberate differences are three:

  1. No hard dependency on Dataview. Heatmap Tracker throws without it and the codeblock processor swallows the throw, so the grid renders as nothing, with no explanation. Here, a block that cannot render says why, in the note.
  2. Body fields count. Heatmap Tracker only reads what Dataview hands it; this reads inline fields itself.
  3. The default palette follows your theme rather than being fixed GitHub greens. See below.

Both differences are covered by tests/parity.test.ts, which names them, so a future change that quietly drifts back into agreement is as visible as one that drifts away.

Colour

The default ramp is built from --habit-accent, which resolves to var(--interactive-accent) unless you set it. So:

  • Change your Obsidian theme and the grid follows it, with no setting to update.
  • Or pick an accent in settings — eight presets that hold up on both a light and a dark ground, or any CSS colour — and every grid, chain, control and focus ring changes at once.
  • Or set accent: on one block, or on one habit, to override just that.

Steps are mixed in oklab against the background, not toward white, which is why one set of numbers reads correctly on both grounds.

Using it

Add habits in Settings → Tally. The tab offers the properties your vault already contains, so you pick rather than type — a typo in a property name is otherwise how you get a silently empty grid.

Habits you want less of

A goal has a direction. At least is the ordinary kind — hit the target and the day counts. At most inverts it: you set a ceiling, and a day is good when you stay under it. A ceiling of 0 is the common case, and it means none at all.

The two kinds are counted differently on purpose, because "did you write something down" and "was the day a success" stop being the same question:

  • A day with no reading at all is a good day on an at-most habit. You do not tap anything to record not smoking, so the streak counts clean days forward from the habit's start date and never asks you to log a zero.
  • Tapping it records a slip, and the tile shows it as one — an error tint and a warning mark, not the accent fill and tick a completed habit gets. A slip is logged, but it is not an achievement, and it should not look like one.
  • The streak is days since the last slip, so it resets to zero the moment you log one, including when you log it today.

Everything else behaves the same. The heatmap still paints, only inverted: a clean day sits at the bottom of the ramp and a bad day climbs it, so a good year still reads as a quiet grid. The year summary keeps readings and good days as separate figures, since on this kind of habit they are close to opposites.

Then, in any note:

```tally
property: Gym
year: 2026
```

Every key is optional. Without one, the block uses your first habit, the current year, your daily-note folder and your accent.

Key Meaning
property Field name, or a list of them to sum
path Folder to search. Defaults to your daily-note folder
tags Note must carry one of these
year Defaults to this year
title Defaults to the habit's label
accent Any CSS colour, for this block only
paletteName theme, default or danger
separateMonths Month blocks, or one continuous run

Commands

Command What it does
Tally: Open the tracker Opens the tracker as a tab. Settings can put it in the right sidebar instead
Tally: Log today Opens the tracker on today, ready to tap
Tally: Export this year's report Writes a markdown report into your export folder
Tally: Export readings as a spreadsheet Writes a CSV, one row per day
Tally: Create a Base for your habits Forces a rewrite of the Base. It normally maintains itself

Exports are written into your vault as ordinary notes, not downloaded, because a download is not something a phone can usefully do with a file.

There is already a Base

You do not run anything to get one. The moment you have a habit, Tally puts a .base in your vault — next to whatever Bases you already keep, so if yours live in Templates/Bases alongside Movies.base and Books.base, that is where Tally.base turns up too. It then follows your habit list: add a habit and a column appears, without you regenerating anything.

It is Obsidian's own table view over the same notes, with a column per habit and these views —

View Rows
All days Every day in your folder, newest first
Last 30 days The recent window
One per habit Days that habit has a reading. A negative habit's view is named "— slips", because that is what its rows are
Fullest days The days you logged the most, sorted by a Logged formula
Nothing logged Days you tracked none of the habits you are building. Habits you are holding down are left out of this one, since an empty reading there is a clean day, not a gap

It is generated rather than shipped as a file in this repo because a Base names a property literally and the properties are yours. It reads your frontmatter directly and stores nothing of its own, so like everything else here it keeps working with Tally uninstalled.

It will not overwrite your edits

A Base is also a thing you edit — drag a column wider and Obsidian writes the new width into the file, which is where the hand-tuned numbers in every one of Obsidian's own templates came from. A plugin that rewrites the file on every settings change would throw those away silently.

So Tally signs its work. Each file it writes carries a fingerprint of its own contents on the second line, and before rewriting it checks that the fingerprint still matches what is on disk. Matches, and the file is exactly as Tally left it, so it is safe to replace. Does not match, and you have edited it — Tally leaves it alone from then on, and Tally: Create a Base for your habits is how you ask for the generated one back.

The fingerprint lives in the file rather than in the plugin's settings, so this survives a settings reset, a sync to another machine, and an uninstall.

Turn the whole thing off, or move the file, under Settings → Tally → Export & import → A Base.

One limit, stated rather than papered over: a Base refers to a property by writing its name bare into an expression, so a habit whose property has spaces in it — Lights out by 11 — cannot appear. None of Obsidian's own thirty Base templates contains such a property and no escape for one is documented, so rather than guess at bracket syntax and hand you a file with a silently empty column, the generator leaves that habit out and names it in a comment at the top of the file. Rename the property to a single word to include it.

On a phone

  • Every control is at least 44px on a phone, the weekday picker included.
  • Every animation sits behind prefers-reduced-motion, and the interface is complete and correct with all of them off.
  • Icons come from Obsidian's own setIcon, so no markup is ever assigned as a string.
  • Layout responds to the pane width through container queries, not the window's, because a sidebar on a wide monitor is still narrow — and a tab on a narrow one is not wide.

On a desktop

The tracker opens as a tab, not in the right sidebar. It has a year grid, a ledger and a card per habit to fit, and the sidebar is the narrowest column in the app. Settings → Tally → Appearance → Opens in puts it back in the sidebar if you would rather have it beside a note; phones always get the tab.

Past about 700px of pane the content caps at 960px and centres, so a wide window reads as a page rather than as a sidebar somebody stretched. That number is where a full year — 53 columns of a 13px cell — stops needing to scroll. Habit cards go two abreast.

Development

npm install
npm test       # 200 assertions, no Obsidian needed
npm run dev    # watch build
npm run check  # tests, production build, release preflight

The tests run the real modules through esbuild against a small Obsidian stub, so they need no vault and no running app:

Suite Covers
tests/parity.test.ts 65 assertions — Dataview's field rules and Heatmap Tracker's data model
tests/reports.test.ts 135 assertions — runs, rates, year summaries, negative habits, the Base and every export format

Cutting a release

The version is the tag, with no leading v:

git tag -a 0.1.0 -m "0.1.0"
git push origin 0.1.0

.github/workflows/release.yml then runs npm run check and, only if it passes, builds and publishes a release with main.js, manifest.json and styles.css attached and notes generated from the commits. Nothing else to click: every BRAT install picks it up, and the community store can see it.

Obsidian's own template stops at a draft, which is worth knowing about because a draft is invisible to everyone — the store scanner reports "no release matches your manifest version" rather than "your release is a draft". Publishing straight from the tag is safe here because the checks run first; a build that fails never reaches gh release create.

The tag must equal version in manifest.json: Obsidian serves the release whose tag matches the manifest at the head of the default branch, and no other. Bump manifest.json, package.json and versions.json together — preflight fails the release if they disagree.

Submission to the community store is done at community.obsidian.md by linking your GitHub account, not by opening a pull request.

scripts/preflight.mjs checks the rules that fail silently when publishing to the community store — version agreement across manifest.json, package.json and versions.json, tag format, description length, no Node builtins in the bundle, no innerHTML, no credential-shaped strings.

Licence

MIT.

HealthExcellent
ReviewSatisfactory
About
Track habits and log with one-tap, mobile-friendly controls, saving every reading directly in your Markdown notes (frontmatter or inline) so data stays readable and queryable if the plugin is removed. View year grids, per-habit heatmaps, month bars and run histories, and preview the exact line that will be written live while editing.
CalendarPropertiesVisualization
Details
Current version
0.1.7
Last updated
2 weeks ago
Created
3 weeks ago
Updates
7 releases
Downloads
258
Compatible with
Obsidian 1.7.2+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
Liam GourLiam Gourvirus7976
GitHubvirus7976
  1. Community
  2. Plugins
  3. Calendar
  4. Tally

Related plugins

Advanced Canvas

Supercharge your canvas experience. Create presentations, flowcharts and more.

Prisma Calendar

Prisma turns any note with a date into a flexible planning system inside Obsidian. There are no rigid schemas or predefined structures — just your data, your rules, fully under your control.

Yearly Glance

Year at a glance - overview of annual events with customizable management options.

TaskNotes

Note-based task management with calendar, pomodoro and time-tracking integration.

Excalidraw

Visual PKM powerhouse. Create and edit Excalidraw drawings.

Meta Bind

Make your notes interactive with inline input fields, metadata displays, and buttons.

Breadcrumbs

Visualise the hierarchy of your vault using a breadcrumb trail or matrix view.

Day Planner

Turn tasks from daily notes, the Tasks plugin, and calendars into time blocks on an editable timeline, with built-in time tracker.

Maps

Adds a map layout to bases so you can display notes as an interactive map view.

Mermaid Tools

Improved Mermaid.js experience: visual toolbar with common elements and more.