From 28e47ed62b3d04a2343cb39ace4b1eaa9343cce3 Mon Sep 17 00:00:00 2001 From: Catherine Chambers Date: Mon, 24 Aug 2026 14:23:57 -0400 Subject: [PATCH 1/5] docs(materialize): mark Accelerated Queries as GA, keep Event Streams in early access Accelerated Queries no longer requires an early access program, so docs now distinguish it from Event Streams, which remains early access. Splits the combined Materialize row in the product comparison matrix into per-feature rows and updates callouts and cross-links accordingly. --- app/authzed/guides/picking-a-product/page.mdx | 38 ++++++++++--------- .../getting-started/overview/page.mdx | 7 ++-- app/spicedb/getting-started/faq/page.mdx | 4 +- .../protecting-a-list-endpoint/page.mdx | 6 ++- lib/changed-pages.json | 36 ++---------------- lib/materialize-features.ts | 7 ++-- 6 files changed, 36 insertions(+), 62 deletions(-) diff --git a/app/authzed/guides/picking-a-product/page.mdx b/app/authzed/guides/picking-a-product/page.mdx index e9e5bd6b..70bbb845 100644 --- a/app/authzed/guides/picking-a-product/page.mdx +++ b/app/authzed/guides/picking-a-product/page.mdx @@ -13,28 +13,32 @@ This document is designed to give a high-level overview of the features supporte ## Feature Matrix -| Feature | [Open Source] | [Cloud] | [Dedicated] | [Enterprise] | -| ---------------------------- | :-----------: | :-----: | :---------: | :----------: | -| Self-Hosted | | | | | -| No-commit pricing | | | | | -| [Materialize (Early Access)] | | | | | -| [Management Dashboard] | | | | | -| [Private Networking] | DIY | | | DIY | -| [Workload Isolation] | DIY | | | DIY | -| [Automated Updates] | DIY | | | DIY | -| [SOC2 Compliance] | DIY | | | DIY | -| [Audit Logging] | | | | | -| [Rate Limiting] | | | | | -| [Multi-Region Deployments] | DIY | | | DIY | -| [Security Embargo] | | | | | -| [Restricted API Access] | DIY | | | | -| [Expedited Support] | | | | | +| Feature | [Open Source] | [Cloud] | [Dedicated] | [Enterprise] | +| -------------------------------- | :-----------: | :-----: | :---------: | :----------: | +| Self-Hosted | | | | | +| No-commit pricing | | | | | +| **[Materialize]** | | | | | +| ↳ [Accelerated Queries] | | | | | +| ↳ [Event Streams (Early Access)] | | | | | +| [Management Dashboard] | | | | | +| [Private Networking] | DIY | | | DIY | +| [Workload Isolation] | DIY | | | DIY | +| [Automated Updates] | DIY | | | DIY | +| [SOC2 Compliance] | DIY | | | DIY | +| [Audit Logging] | | | | | +| [Rate Limiting] | | | | | +| [Multi-Region Deployments] | DIY | | | DIY | +| [Security Embargo] | | | | | +| [Restricted API Access] | DIY | | | | +| [Expedited Support] | | | | | [Cloud]: #cloud [Dedicated]: #dedicated [Enterprise]: #enterprise [Open Source]: #open-source -[Materialize (Early Access)]: /materialize/getting-started/overview +[Materialize]: /materialize/getting-started/overview +[Accelerated Queries]: /materialize/getting-started/overview#accelerated-queries +[Event Streams (Early Access)]: /materialize/getting-started/overview#event-streams [Audit Logging]: ../concepts/audit-logging [Automated Updates]: ../concepts/update-channels [Management Dashboard]: ../concepts/management-dashboard diff --git a/app/materialize/getting-started/overview/page.mdx b/app/materialize/getting-started/overview/page.mdx index de97d7d8..993dcaa7 100644 --- a/app/materialize/getting-started/overview/page.mdx +++ b/app/materialize/getting-started/overview/page.mdx @@ -12,7 +12,7 @@ AuthZed Materialize takes inspiration from the Leopard index component described Much like the concept of a materialized view in relational databases, AuthZed Materialize is a service that you configure with a list of permissions that you want it to precompute, and it will calculate how those permissions change after relationships are written (specifically, when those relationships affect a subject's membership in a permission set or a set's permission on a specific resource), or when a new schema is written. -Materialize puts that precomputed data to work in two ways: **Accelerated Queries** and **Event Streams**, both currently in early access. Every other piece of Materialize content belongs to one of these two features. Look for badges at the top of the page, which link back to the feature it belongs to. +Materialize puts that precomputed data to work in two ways: **Accelerated Queries**, generally available to AuthZed [Dedicated] users, and **Event Streams**, currently in early access. Every other piece of Materialize content belongs to one of these two features. Look for badges at the top of the page, which link back to the feature it belongs to. - Accelerated Queries is available to users of AuthZed [Dedicated] as part of an early access - program. Don't hesitate to get in touch with your AuthZed account team if you would like to - participate. + Accelerated Queries is generally available to users of AuthZed [Dedicated]. Get in touch with your + AuthZed account team to get started. Accelerated Queries use Materialize's precomputed permissions cache to answer the SpiceDB queries your application already makes, without changing how you call SpiceDB: diff --git a/app/spicedb/getting-started/faq/page.mdx b/app/spicedb/getting-started/faq/page.mdx index 48d1df2e..779ed039 100644 --- a/app/spicedb/getting-started/faq/page.mdx +++ b/app/spicedb/getting-started/faq/page.mdx @@ -59,9 +59,9 @@ There are three approaches for filtering resources based on whether users have a 2. **CheckBulkPermissions** - Use when accessible resources are too large for LookupResources. Fetch a page of candidate results from your database, then call `CheckBulkPermissions` to determine which ones the user can access. Keep iterating until you have a full page of permitted results. This works well with cursor-based pagination and search interfaces. -3. **Materialize** (Early Access) - For maximum scalability with large datasets or high traffic. Materialize watches permission changes in SpiceDB and maintains a local denormalized view of user permissions, allowing you to use simple database JOINs for filtering. +3. **Materialize's Event Streams** (Early Access) - For maximum scalability with large datasets or high traffic. Materialize watches permission changes in SpiceDB and maintains a local denormalized view of user permissions, allowing you to use simple database JOINs for filtering. -Choose based on your scale: start with LookupResources, move to CheckBulkPermissions when needed, and consider Materialize for the highest performance requirements. [Learn more] +Choose based on your scale: start with LookupResources, move to CheckBulkPermissions when needed, and consider Materialize's Event Streams for the highest performance requirements. [Learn more] [Learn more]: ../modeling/protecting-a-list-endpoint diff --git a/app/spicedb/modeling/protecting-a-list-endpoint/page.mdx b/app/spicedb/modeling/protecting-a-list-endpoint/page.mdx index 5cbb8461..f934d60e 100644 --- a/app/spicedb/modeling/protecting-a-list-endpoint/page.mdx +++ b/app/spicedb/modeling/protecting-a-list-endpoint/page.mdx @@ -93,8 +93,9 @@ to the point where checking them via bulk check is relatively easy. ## Using Materialize - Materialize is currently in Early Access. Additional documentation and product information will be - coming soon. In the meantime, if you're interested, [schedule a call!][Schedule Call] + This pattern uses Materialize's Event Streams feature, currently in early access for AuthZed + Dedicated users. See the [Materialize overview] to learn more, or [schedule a call!][Schedule + Call] [Authzed Materialize] is Authzed's version of the [Leopard cache] referenced in the Zanzibar paper, which provides a denormalized view @@ -113,6 +114,7 @@ under the above two approaches, we recommend giving Authzed Materialize a try. [Schedule Call]: https://authzed.com/call [Leopard cache]: https://authzed.com/zanzibar/2IoYDUFMAE:0:T +[Materialize overview]: /materialize/getting-started/overview#event-streams ## Other Considerations diff --git a/lib/changed-pages.json b/lib/changed-pages.json index 28a924b4..9052da4c 100644 --- a/lib/changed-pages.json +++ b/lib/changed-pages.json @@ -1,44 +1,14 @@ { - "/materialize/api/client-sdks": { - "status": "updated" - }, - "/materialize/api/download-permission-sets": { - "status": "new" - }, - "/materialize/api/lookup-permission-sets": { - "status": "updated" - }, - "/materialize/api/watch-permission-sets": { - "status": "updated" - }, - "/materialize/concepts/hydration": { - "status": "new" - }, - "/materialize/concepts/managing-client-state": { - "status": "updated" - }, - "/materialize/concepts/permission-set-lifecycle": { - "status": "updated" - }, - "/materialize/concepts/permission-sets": { - "status": "updated" - }, - "/materialize/concepts/snapshots": { - "status": "updated" - }, - "/materialize/concepts/watched-permissions": { - "status": "new" - }, - "/materialize/getting-started/limitations": { + "/authzed/guides/picking-a-product": { "status": "updated" }, "/materialize/getting-started/overview": { "status": "updated" }, - "/materialize/guides/recommended-architecture": { + "/spicedb/getting-started/faq": { "status": "updated" }, - "/materialize/guides/relational-database": { + "/spicedb/modeling/protecting-a-list-endpoint": { "status": "updated" } } diff --git a/lib/materialize-features.ts b/lib/materialize-features.ts index aab8454f..e385caae 100644 --- a/lib/materialize-features.ts +++ b/lib/materialize-features.ts @@ -21,11 +21,10 @@ export const FEATURES = { export type FeatureKey = keyof typeof FEATURES; -/* Every feature is in early access today, so the note's text doesn't vary. - It's per-feature anyway because the note is colour-matched to its pill — - and because these will go GA on different dates. */ +/* Per-feature because the note is colour-matched to its pill, and because + these go GA on different dates — Accelerated Queries reached GA first. */ export const EARLY_ACCESS: Record = { - "accelerated-queries": true, + "accelerated-queries": false, "event-streams": true, }; From cb276ac7d520a8bd175fb832fb79a29803115e93 Mon Sep 17 00:00:00 2001 From: Corey Thomas <125082303+Corey-T1000@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:36:22 -0400 Subject: [PATCH 2/5] docs(picking-a-product): style the feature matrix like the marketing comparison tables Absences were a red x, the same mark the docs use for a denied permission check. In a product matrix a tier not carrying a line is an absence, not a failure, so it now reads as a muted dash: the treatment the pricing and Support comparison tables on authzed.com settled on. is unchanged where it still means a real negative. Materialize becomes a group label over its two feature rows instead of a third scored row that repeated the same marks. --- app/authzed/guides/picking-a-product/page.mdx | 36 +++++++++---------- components/feature-icon.css | 20 +++++++++++ components/feature-icon.tsx | 17 ++++++++- mdx-components.ts | 3 +- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/app/authzed/guides/picking-a-product/page.mdx b/app/authzed/guides/picking-a-product/page.mdx index 70bbb845..58a818d2 100644 --- a/app/authzed/guides/picking-a-product/page.mdx +++ b/app/authzed/guides/picking-a-product/page.mdx @@ -13,24 +13,24 @@ This document is designed to give a high-level overview of the features supporte ## Feature Matrix -| Feature | [Open Source] | [Cloud] | [Dedicated] | [Enterprise] | -| -------------------------------- | :-----------: | :-----: | :---------: | :----------: | -| Self-Hosted | | | | | -| No-commit pricing | | | | | -| **[Materialize]** | | | | | -| ↳ [Accelerated Queries] | | | | | -| ↳ [Event Streams (Early Access)] | | | | | -| [Management Dashboard] | | | | | -| [Private Networking] | DIY | | | DIY | -| [Workload Isolation] | DIY | | | DIY | -| [Automated Updates] | DIY | | | DIY | -| [SOC2 Compliance] | DIY | | | DIY | -| [Audit Logging] | | | | | -| [Rate Limiting] | | | | | -| [Multi-Region Deployments] | DIY | | | DIY | -| [Security Embargo] | | | | | -| [Restricted API Access] | DIY | | | | -| [Expedited Support] | | | | | +| Feature | [Open Source] | [Cloud] | [Dedicated] | [Enterprise] | +| -------------------------------- | :-----------: | :--------: | :---------: | :----------: | +| Self-Hosted | | | | | +| No-commit pricing | | | | | +| **[Materialize]** | | | | | +| ↳ [Accelerated Queries] | | | | | +| ↳ [Event Streams (Early Access)] | | | | | +| [Management Dashboard] | | | | | +| [Private Networking] | DIY | | | DIY | +| [Workload Isolation] | DIY | | | DIY | +| [Automated Updates] | DIY | | | DIY | +| [SOC2 Compliance] | DIY | | | DIY | +| [Audit Logging] | | | | | +| [Rate Limiting] | | | | | +| [Multi-Region Deployments] | DIY | | | DIY | +| [Security Embargo] | | | | | +| [Restricted API Access] | DIY | | | | +| [Expedited Support] | | | | | [Cloud]: #cloud [Dedicated]: #dedicated diff --git a/components/feature-icon.css b/components/feature-icon.css index 6cea4f29..55b81aba 100644 --- a/components/feature-icon.css +++ b/components/feature-icon.css @@ -35,3 +35,23 @@ html.dark .feature-yes svg { .feature-no svg { stroke: hsl(351 55% 53%); } + +/* Absent — muted dash, no disc. Reads as "nothing here" rather than "no". */ +.feature-absent { + color: hsl(280 4% 62%); + font-size: 1.125rem; + line-height: 1; +} +html.dark .feature-absent { + color: hsl(279 9% 40%); +} + +.feature-sr { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} diff --git a/components/feature-icon.tsx b/components/feature-icon.tsx index a5222451..19d5e64b 100644 --- a/components/feature-icon.tsx +++ b/components/feature-icon.tsx @@ -2,7 +2,13 @@ import "./feature-icon.css"; /* Feature-matrix marks for docs tables — Lucide check / x glyphs in Sandworm colors, matching the marketing pricing comparison. Theme-aware styling lives - in feature-icon.css. Used in MDX as / (mdx-components.ts). */ + in feature-icon.css. Used in MDX as / / + (mdx-components.ts). + + is a red cross: a real negative (a permission check that denied). + is a muted dash: a tier that simply doesn't carry a line. In a + product matrix an absence is not a failure, so it should not read as one. + Same rule as the marketing pricing/support comparison (SupportComparison). */ export function Yes() { return ( @@ -41,3 +47,12 @@ export function No() { ); } + +export function Absent() { + return ( + + + Not included + + ); +} diff --git a/mdx-components.ts b/mdx-components.ts index 699f653a..f7105f6a 100644 --- a/mdx-components.ts +++ b/mdx-components.ts @@ -1,6 +1,6 @@ import { useMDXComponents as getDocsMDXComponents } from "nextra-theme-docs"; import type { Component } from "react"; -import { Yes, No } from "@/components/feature-icon"; +import { Yes, No, Absent } from "@/components/feature-icon"; import { FeatureBadge } from "@/components/feature-badge"; const docsComponents = getDocsMDXComponents(); @@ -9,6 +9,7 @@ export const useMDXComponents = (components?: Component) => ({ ...docsComponents, Yes, No, + Absent, FeatureBadge, ...components, }); From 97a89f1dc0359918d03fc3acc907d0a93e2dfc9a Mon Sep 17 00:00:00 2001 From: Corey Thomas <125082303+Corey-T1000@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:45:26 -0400 Subject: [PATCH 3/5] docs(picking-a-product): FeatureMatrix wrapper so the table reads like the web comparison tables Mono row labels, horizontal rules only, group-label row, plain-text column headers, row hover. Unlayered CSS so it overrides Nextra's x: utilities. --- app/authzed/guides/picking-a-product/page.mdx | 22 +-- components/feature-matrix.css | 128 ++++++++++++++++++ components/feature-matrix.tsx | 12 ++ mdx-components.ts | 2 + 4 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 components/feature-matrix.css create mode 100644 components/feature-matrix.tsx diff --git a/app/authzed/guides/picking-a-product/page.mdx b/app/authzed/guides/picking-a-product/page.mdx index 58a818d2..4273e316 100644 --- a/app/authzed/guides/picking-a-product/page.mdx +++ b/app/authzed/guides/picking-a-product/page.mdx @@ -13,24 +13,28 @@ This document is designed to give a high-level overview of the features supporte ## Feature Matrix + + | Feature | [Open Source] | [Cloud] | [Dedicated] | [Enterprise] | | -------------------------------- | :-----------: | :--------: | :---------: | :----------: | -| Self-Hosted | | | | | -| No-commit pricing | | | | | +| Self-Hosted | | | | | +| No-commit pricing | | | | | | **[Materialize]** | | | | | -| ↳ [Accelerated Queries] | | | | | -| ↳ [Event Streams (Early Access)] | | | | | -| [Management Dashboard] | | | | | +| ↳ [Accelerated Queries] | | | | | +| ↳ [Event Streams (Early Access)] | | | | | +| [Management Dashboard] | | | | | | [Private Networking] | DIY | | | DIY | | [Workload Isolation] | DIY | | | DIY | | [Automated Updates] | DIY | | | DIY | | [SOC2 Compliance] | DIY | | | DIY | -| [Audit Logging] | | | | | -| [Rate Limiting] | | | | | +| [Audit Logging] | | | | | +| [Rate Limiting] | | | | | | [Multi-Region Deployments] | DIY | | | DIY | -| [Security Embargo] | | | | | +| [Security Embargo] | | | | | | [Restricted API Access] | DIY | | | | -| [Expedited Support] | | | | | +| [Expedited Support] | | | | | + + [Cloud]: #cloud [Dedicated]: #dedicated diff --git a/components/feature-matrix.css b/components/feature-matrix.css new file mode 100644 index 00000000..cafa575a --- /dev/null +++ b/components/feature-matrix.css @@ -0,0 +1,128 @@ +/* Feature matrix — the docs twin of authzed.com's pricing/Support comparison + tables (web: pricing/TableGroup.tsx + SupportComparison.tsx). Unlayered so + it wins over Nextra's `x:` table utilities without !important. */ + +.feature-matrix { + --fm-rule: hsl(280 5% 84%); + --fm-hover: hsl(300 8% 96%); + --fm-label: hsl(280 6% 22%); + --fm-head: hsl(280 10% 12%); + --fm-muted: hsl(280 4% 48%); + margin-top: 1.5rem; + overflow-x: auto; +} +html.dark .feature-matrix { + --fm-rule: hsl(279 9% 22%); + --fm-hover: hsl(279 12% 11%); + --fm-label: hsl(280 6% 82%); + --fm-head: hsl(300 6% 95%); + --fm-muted: hsl(280 4% 55%); +} + +.feature-matrix table { + display: table; + width: 100%; + margin: 0; + padding: 0; + border-collapse: collapse; + overflow: visible; +} + +.feature-matrix tr { + margin: 0; + padding: 0; + border: 0; + border-top: 1px solid var(--fm-rule); + border-bottom: 1px solid var(--fm-rule); + background: transparent; +} +.feature-matrix thead tr { + border-top: 0; +} +.feature-matrix tbody tr:hover { + background: var(--fm-hover); +} + +.feature-matrix th, +.feature-matrix td { + margin: 0; + padding: 1rem 0.5rem; + border: 0; + vertical-align: middle; +} + +/* Column headers: plain text, not link-orange. The link still works. */ +.feature-matrix thead th { + padding-top: 0.75rem; + padding-bottom: 1rem; + font-family: inherit; + font-size: 1.125rem; + font-weight: 400; + color: var(--fm-head); + text-align: center; +} +.feature-matrix thead th:first-child { + padding-left: 1.5rem; + text-align: left; +} +.feature-matrix thead th a { + color: inherit; + font-weight: inherit; + text-decoration: none; +} +.feature-matrix thead th a:hover { + text-decoration: underline; + text-underline-offset: 0.2em; +} + +/* Row labels: mono, like the web tables. Links stay links, quietly. */ +.feature-matrix tbody td:first-child { + padding-left: 1.5rem; + font-family: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace; + font-size: 0.875rem; + color: var(--fm-label); + text-align: left; + white-space: nowrap; +} +.feature-matrix tbody td:first-child a { + color: inherit; + text-decoration: none; +} +.feature-matrix tbody td:first-child a:hover { + text-decoration: underline; + text-underline-offset: 0.2em; +} +/* Nextra appends an arrow icon to external links; keep it, just quiet it. */ +.feature-matrix tbody td:first-child a svg, +.feature-matrix thead th a svg { + opacity: 0.6; +} + +/* Value cells: centred, ruled between columns, never against the label. */ +.feature-matrix tbody td:not(:first-child) { + border-left: 1px solid var(--fm-rule); + font-size: 0.875rem; + text-align: center; + color: var(--fm-label); +} +.feature-matrix tbody td:not(:first-child) .feature-mark { + vertical-align: middle; +} + +/* Group-label row: a bold first cell and nothing else in the row. + Reads as a section header (web: RowGroup title), not a scored feature. */ +.feature-matrix tbody tr:has(> td:first-child > strong) { + background: transparent; +} +.feature-matrix tbody tr:has(> td:first-child > strong) td:first-child { + padding-top: 1rem; + padding-bottom: 0.75rem; + font-size: 1rem; + font-weight: 300; +} +.feature-matrix tbody tr:has(> td:first-child > strong) strong { + font-weight: inherit; +} +.feature-matrix tbody tr:has(> td:first-child > strong) td:not(:first-child) { + border-left-color: transparent; +} diff --git a/components/feature-matrix.tsx b/components/feature-matrix.tsx new file mode 100644 index 00000000..349d09dd --- /dev/null +++ b/components/feature-matrix.tsx @@ -0,0 +1,12 @@ +import "./feature-matrix.css"; +import type { ReactNode } from "react"; + +/* Wraps a markdown feature matrix so it reads like the pricing and Support + comparison tables on authzed.com instead of Nextra's default zebra table: + mono row labels, horizontal rules only, a group-label row for a feature + family (a bold first cell with the rest empty), row hover, plain-text + column headers. Used in MDX as …table… + (mdx-components.ts). Styling lives in feature-matrix.css. */ +export function FeatureMatrix({ children }: { children: ReactNode }) { + return
{children}
; +} diff --git a/mdx-components.ts b/mdx-components.ts index f7105f6a..2d8cdbf1 100644 --- a/mdx-components.ts +++ b/mdx-components.ts @@ -2,6 +2,7 @@ import { useMDXComponents as getDocsMDXComponents } from "nextra-theme-docs"; import type { Component } from "react"; import { Yes, No, Absent } from "@/components/feature-icon"; import { FeatureBadge } from "@/components/feature-badge"; +import { FeatureMatrix } from "@/components/feature-matrix"; const docsComponents = getDocsMDXComponents(); @@ -11,5 +12,6 @@ export const useMDXComponents = (components?: Component) => ({ No, Absent, FeatureBadge, + FeatureMatrix, ...components, }); From 3376dbad8c0f18a60295d6468be02bc170c9bb0d Mon Sep 17 00:00:00 2001 From: Corey Thomas <125082303+Corey-T1000@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:49:15 -0400 Subject: [PATCH 4/5] docs(picking-a-product): underline the matrix links so the label column reads as clickable --- components/feature-matrix.css | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/components/feature-matrix.css b/components/feature-matrix.css index cafa575a..220689f7 100644 --- a/components/feature-matrix.css +++ b/components/feature-matrix.css @@ -8,6 +8,8 @@ --fm-label: hsl(280 6% 22%); --fm-head: hsl(280 10% 12%); --fm-muted: hsl(280 4% 48%); + --fm-link-rule: hsl(280 5% 72%); + --fm-link: hsl(var(--sand-300)); margin-top: 1.5rem; overflow-x: auto; } @@ -17,6 +19,8 @@ html.dark .feature-matrix { --fm-label: hsl(280 6% 82%); --fm-head: hsl(300 6% 95%); --fm-muted: hsl(280 4% 55%); + --fm-link-rule: hsl(279 8% 36%); + --fm-link: hsl(var(--sand-300)); } .feature-matrix table { @@ -65,14 +69,20 @@ html.dark .feature-matrix { padding-left: 1.5rem; text-align: left; } -.feature-matrix thead th a { +.feature-matrix thead th a, +.feature-matrix tbody td:first-child a { color: inherit; font-weight: inherit; - text-decoration: none; -} -.feature-matrix thead th a:hover { text-decoration: underline; - text-underline-offset: 0.2em; + text-decoration-color: var(--fm-link-rule); + text-decoration-thickness: 1px; + text-underline-offset: 0.3em; + transition: color 120ms, text-decoration-color 120ms; +} +.feature-matrix thead th a:hover, +.feature-matrix tbody td:first-child a:hover { + color: var(--fm-link); + text-decoration-color: currentColor; } /* Row labels: mono, like the web tables. Links stay links, quietly. */ @@ -84,14 +94,6 @@ html.dark .feature-matrix { text-align: left; white-space: nowrap; } -.feature-matrix tbody td:first-child a { - color: inherit; - text-decoration: none; -} -.feature-matrix tbody td:first-child a:hover { - text-decoration: underline; - text-underline-offset: 0.2em; -} /* Nextra appends an arrow icon to external links; keep it, just quiet it. */ .feature-matrix tbody td:first-child a svg, .feature-matrix thead th a svg { From 8989749a65a8ce564afbbbd37f47569ab70a98b9 Mon Sep 17 00:00:00 2001 From: Catherine Chambers Date: Thu, 10 Sep 2026 10:39:29 -0400 Subject: [PATCH 5/5] style: fix oxfmt formatting in feature-matrix.css Co-Authored-By: Claude Sonnet 5 --- components/feature-matrix.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/feature-matrix.css b/components/feature-matrix.css index 220689f7..fb59df8c 100644 --- a/components/feature-matrix.css +++ b/components/feature-matrix.css @@ -77,7 +77,9 @@ html.dark .feature-matrix { text-decoration-color: var(--fm-link-rule); text-decoration-thickness: 1px; text-underline-offset: 0.3em; - transition: color 120ms, text-decoration-color 120ms; + transition: + color 120ms, + text-decoration-color 120ms; } .feature-matrix thead th a:hover, .feature-matrix tbody td:first-child a:hover {