Styling a base

[Moderator note: This was originally posted in reply to Bases: Hide type icon in column headers for tables - #3 by Maja]

Hi there, thank you for that solution, it was really helpful for me, but I do want to make a few more adjustments to my table, I want to make the border between the columns disappear but only between the first 3 columns and below the headers and above the bottom border (kinda like merge and centre), I also want make it a bit more compact, because at the moment there is too much wasted space. If you look at this image I’m sure you’ll understand.

This is bit more complicated, as themes seem to adjust base borders differently. You can adjust the width by dragging, but we can centre everything and remove the lines. The below works for obsidian without a theme, hopefully it works for whatever you use:

/* Removes horizontal lines between the left 3 columns */
.bases-tr .bases-td:nth-child(2),
.bases-tr .bases-td:nth-child(3) {
    box-shadow: 0 0 0 0 !important;
}

    /* Centre checkboxes, depending on your setup it may not change anything. */
div.bases-table-cell.bases-metadata-value.metadata-property-value:has(input.metadata-input-checkbox) {
    justify-content: center;
}

    /* Removes whatever padding there may be around the checkbox, 
depending on your setup it may not change anything. */
input[type=checkbox].metadata-input-checkbox {
    margin: 0;
}

    /* Centre header */
.bases-thead .bases-table-header {
    justify-content: center;
}

If you only want to merge the first three columns on some files, you can change the first one to the below. So it becomes a css class you use in any note you want to merge the first three columns. If you want to merge more columns, simply add more nth-child lines.

/* Removes horizontal lines between the left 3 columns */
.bases-merge-first-columns .bases-tr .bases-td:nth-child(2),
.bases-merge-first-columns .bases-tr .bases-td:nth-child(3) {
    box-shadow: 0 0 0 0 !important;
}

Thanks a lot for your original snippet, it actually got me most of the way there by throwing me down yet another rabbit hole. So far, I’ve managed to refine it so that only the first three formula columns have centred headers while the rest stay left-aligned.

At first, I was using nth-child selectors to remove the dividers between the first three visible columns, but that caused them to disappear when panning horizontally. Whichever three columns were on screen lost their borders. After some trial and error, I switched to targeting the columns directly by their data-property attributes, which fixed that issue.

For a while, I thought there was still some inconsistency between two of my Bases, but it turned out to be because the original formula names were different. Once I deleted and recreated those formulas from scratch using consistent names (formula.1, formula.2, formula.3), everything lined up perfectly.

So now everything’s working — checkboxes centred, first three headers centred, others left-aligned, borders fixed. The only thing left is that I’d like the first three columns to be a little closer together. They’re already at the minimum draggable width, so I’ll probably need to handle that purely with CSS.

Here’s what it looks like now, if you need any more info, let me know.

And here’s the current working CSS:


.bases-table-header-icon {
    display: none;
}

/* Remove dividers for the actual first three formula columns, regardless of order */
.bases-view [data-property="formula.1"],
.bases-view [data-property="formula.2"],
.bases-view [data-property="formula.3"] {
  box-shadow: none !important;
  border-inline-end: none !important;
}
.bases-view [data-property="formula.1"]::after,
.bases-view [data-property="formula.2"]::after,
.bases-view [data-property="formula.3"]::after {
  content: none !important;
}
.bases-view [data-property="formula.1"] .bases-table-header-resizer,
.bases-view [data-property="formula.2"] .bases-table-header-resizer,
.bases-view [data-property="formula.3"] .bases-table-header-resizer {
  opacity: 0 !important;
  pointer-events: none;
}

/* checkbox-centred */
.bases-view .bases-tbody .bases-tr > *:has(input[type="checkbox"]) {
  text-align: center !important;
}
.bases-view input[type="checkbox"] {
  display: inline-block;
  margin: 0 auto !important;
  vertical-align: middle;
}

/* Centre headers of the three formula columns */
.bases-view .bases-thead .bases-td.mod-formula .bases-table-header {
  justify-content: center !important;
  align-items: center !important;
}
.bases-view .bases-thead .bases-td.mod-formula .bases-table-header-name {
  text-align: center !important;
}

/* Everything else left-aligned */
.bases-view .bases-thead .bases-td:not(.mod-formula) .bases-table-header {
  justify-content: flex-start !important;
  align-items: center !important;
}
.bases-view .bases-thead .bases-td:not(.mod-formula) .bases-table-header-name {
  text-align: left !important;
}


1 Like

Glad you got what you wanted! Targeting specific data properties was a smart move, I might use that for some other things. Like centring progress bars.

1 Like

Final and Working Edition of My Progression-Based To-Do Setup in Bases

Alright, so here’s the final and working version of my Bases setup.

Before I get into why it works, I’ll explain what exactly I was trying to build.

The whole thing is meant to be a progression-based to-do list, where each item has three Boolean checkpoints (1, 2, and 3) that can be ticked or unticked directly from YAML frontmatter or from the Bases table itself. Each of these is just a normal checkbox property — true or false — nothing fancy.

Here’s the simple tick-count formula I’m using:

if(note["1"].isTruthy(), 1, 0) +
if(note["2"].isTruthy(), 1, 0) +
if(note["3"].isTruthy(), 1, 0)

This adds up to a number between 0 and 3, depending on how many checkboxes are ticked.

I’m sorting the table first by Tick Count (ascending) and then by Name (A→Z) — so 0 comes before 1, and everything stays nicely ordered alphabetically within each tick count group.

Previously, this was done using a pretty bloated system that relied on tags to simulate ticks. I had to add #t1, #t2, #t3, or #from3 (The idea behind #from3 was to make it so that when something moved from three ticks back to two, it would go to the bottom of the two-tick section rather than the top.) manually, which worked, but it was clunky and got messy fast once tags started piling up.

Now I just physically tick the boxes. No tags. No extra formulas.

I might eventually refine it further — for example, if I tick checkpoint 2 or 3, it could automatically tick the ones before it — but honestly, it already feels good enough as it is, so I’m leaving that for later.

The other major improvement is in the CSS
The old version was extremely bloated — not the formulas themselves, but the CSS file. It used loads of flex rules and extra layers that were slowing everything down. I stripped almost all of that out and rebuilt it from scratch.

Here’s the previous working version (bloated as heck and made through lots of arguments with GPT who kept repeating the same thing and doing my head in, I had to read through the elements in the console to push GPT in the right direction, the issue i was asking its help for was the width of the columns but it didn’t know how to fix it and only after looking through the console as I tinkered, did I came up with the right idea of changing the global width restrictions. I gave the idea to GPT and it gave me a code that worked but was unnecessarily bloated. Honestly, from here on I’m limiting my use of AI, although it did push me to learn to do it myself out of sheer frustration, which I guess is one way to learn.):

.bases-table-header-icon {
    display: none;
}

/* Remove dividers for the actual first three formula columns, regardless of order */
.bases-view [data-property="formula.1"],
.bases-view [data-property="formula.2"],
.bases-view [data-property="formula.3"] {
  box-shadow: none !important;
  border-inline-end: none !important;
}
.bases-view [data-property="formula.1"]::after,
.bases-view [data-property="formula.2"]::after,
.bases-view [data-property="formula.3"]::after {
  content: none !important;
}
.bases-view [data-property="formula.1"] .bases-table-header-resizer,
.bases-view [data-property="formula.2"] .bases-table-header-resizer,
.bases-view [data-property="formula.3"] .bases-table-header-resizer {
  opacity: 0 !important;
  pointer-events: none;
}

/* centre Booleans or checkboxes */
.bases-view .bases-tbody .bases-tr > *:has(input[type="checkbox"]),
.bases-view .bases-tbody .bases-tr > *:has(.bases-boolean),
.bases-view .bases-tbody .bases-tr > *:has(.mod-boolean) {
  text-align: center !important;
}

.bases-view input[type="checkbox"],
.bases-view .bases-boolean,
.bases-view .mod-boolean {
  display: inline-block !important;
  margin: 0 auto !important;
  vertical-align: middle !important;
}

/* Centre headers of the three formula columns */
.bases-view .bases-thead .bases-td.mod-formula .bases-table-header {
  justify-content: center !important;
  align-items: center !important;
}
.bases-view .bases-thead .bases-td.mod-formula .bases-table-header-name {
  text-align: center !important;
}

/* Everything else left-aligned */
.bases-view .bases-thead .bases-td:not(.mod-formula) .bases-table-header {
  justify-content: flex-start !important;
  align-items: center !important;
}
.bases-view .bases-thead .bases-td:not(.mod-formula) .bases-table-header-name {
  text-align: left !important;
}

/* kill min-width CSS variables used by Bases */
.bases-view {
  --bases-table-column-min-width: 0px !important;
  --table-column-min-width: 0px !important;
}

/* remove hard min-widths on header + resizer layers */
.bases-view .bases-thead .bases-td,
.bases-view .bases-thead .bases-td .bases-table-header,
.bases-view .bases-thead .bases-td .bases-table-header-label,
.bases-view .bases-thead .bases-td .bases-table-header-name,
.bases-view .bases-table-header-resizer {
  min-width: 0 !important;
  min-inline-size: 0 !important;
}

/* allow columns to shrink below 40 px when dragged */
.bases-view .bases-thead .bases-td {
  flex-shrink: 1 !important;
}

/* optional: smaller default width for first three */
.bases-view .bases-td[data-property="formula.1"],
.bases-view .bases-td[data-property="formula.2"],
.bases-view .bases-td[data-property="formula.3"] {
  width: 28px !important;
  min-width: 28px !important;
  max-width: 28px !important;
  flex: 0 0 28px !important;
}

/* centre the checkbox inside formula cells by targeting the real node */
.bases-view .bases-tbody .bases-td[data-property^="formula."] > .bases-table-cell.bases-rendered-value:has(> input[type="checkbox"]) {
  display: flex !important;
  justify-content: center !important;
  align-items: center !important;
  width: 70% !important;
  height: 100% !important;
  padding: 0 !important;
  margin: 0 !important;
  box-sizing: border-box !important;
}

.bases-view .bases-tbody .bases-td[data-property^="formula."] > .bases-table-cell.bases-rendered-value > input[type="checkbox"] {
  display: inline-block !important;
  margin: 0 !important;
  vertical-align: middle !important;
}

The new CSS is lightweight. It hides the header icons, keeps the columns clean, centres everything that needs to be centred, and just generally looks and performs better.
I only kept what was absolutely necessary — nothing redundant. AND I DID IT ALL ON MY OWN:

/* === 1. HIDES HEADER ICONS === */
.bases-table-header-icon {
    display: none;
}

/* === 2. REMOVES DIVIDERS FOR FIRST THREE NOTE COLUMNS === */
.bases-view [data-property="note.1"],
.bases-view [data-property="note.2"],
.bases-view [data-property="note.3"] {
  box-shadow: none !important;
  border-inline-end: none !important;
}

/* === 3. CENTRES CHECKBOXES IN BODY CELLS === */
.bases-view input[type="checkbox"] {
  display: inline-block !important;
  margin: 0 auto !important;
  vertical-align: middle !important;
}

/* === 4. ADDS LEFT MARGIN TO THIRD CHECKBOX COLUMN (Stabalises it a lot better, fits perfectly if the first two column widths are 20px and the last is 23px, ) === */
.bases-view [data-property="note.3"] .bases-table-cell input[type="checkbox"] {
  margin-left: 2px !important; /* adjust as needed */
}

/* === 5. CENTREs HEADERS OF THE THREE CHECKBOX COLUMNS === */
.bases-view .bases-thead .bases-td:has(.bases-table-header-icon .lucide-check-square) .bases-table-header {
  justify-content: center !important;
}

/* === 6. GLOBAL TABLE CONFIGURATION (COLUMN MIN-WIDTH) === */
.bases-view {
  --bases-table-column-min-width: 20px !important;
}

One cool side effect of switching to true/false booleans is that Obsidian visually distinguishes between:

  • a box that’s currently ticked (true), and
  • a box that’s been unticked (false).
  • a box that’s never been ticked (empty state),

Respectively:

So even without the old “from3” logic, I can still tell which items I’ve actively unticked — and that’s basically replaced the need for all the extra sorting formulas I used before.

Performance is miles better now, and the setup works exactly how I wanted:
simple, responsive, and progression-based without any tag clutter.

But, um, please do tell me about these progress bars, they sound interesting.