
Adopting Safari Technology Preview 256 Without Breaking Stable Safari
On this page
Introduction
Adopting Safari Technology Preview 256 without breaking stable Safari comes down to one habit: treat a TP build as a signal about where WebKit is heading, not as a support matrix entry. This post covers how to extract the TP 255 → 256 delta from Apple's own release notes, sort every change into four adoption buckets, back each adoption with behavior-based feature-detection and a tested fallback, and run a WebKit CI lane that catches the regressions preview-driven code actually produces.
The failure mode is boring: someone reads a Safari Technology Preview release note, finds a useful API, drops it into a shared component, and ships. Six weeks later a stable-Safari user reports an unstyled form, a collapsed grid, or a sticky header that scrolls away. No console error. No red test. The code was correct — the assumption underneath it was not.
A TP build previews an engine Apple has not committed to shipping in that shape. Treat it as a target and you end up maintaining a shim for a feature that gets reworked before it ever reaches a release Safari.
The position here is narrow and, I think, defensible: adopt a TP feature only when a second engine ships the same behavior, and never on the strength of a TP release note alone. Everything below is machinery for making that rule cheap to follow — a repeatable delta list, behavior-based detection, and a WebKit CI lane that catches the regressions this kind of adoption actually produces in stable WebKit.
What the Public Record Actually Confirms About Safari Technology Preview 256
Confirmed: Apple publishes per-build WebKit release notes, and a third-party catalog compares TP 256 against TP 255
Two things are established, and it is worth keeping them separate from everything else.
First, Apple publishes release notes for each Safari Technology Preview build, grouped by area (CSS, JavaScript, Web API, Web Inspector, and so on). Feature-level write-ups also land on the WebKit blog. That is the primary record, and it is the only thing worth diffing against.
Second, a community write-up exists: shattered.io published "Safari Technology Preview 256: What Changed Since 255" on 5 November 2026, available here via a Google News redirect. Secondary context — a catalog of changes, not an authority on semantics.
Not confirmed here: the TP 256 feature list, version ranges, and pref state
This is where a lot of posts would start listing features. I am not going to, because the source material I have does not carry them, and a plausible invented list is worse than useless — it becomes a checklist someone acts on.
What I do not have at the time of writing:
- The itemised diff between build 255 and build 256.
- The WebKit build identifier or Safari version string attached to TP 256.
- Which items are on by default versus gated behind a developer preference or a feature flag.
- Whether any item has since been reverted on trunk.
If a claim in this article depends on any of those, it is flagged as inference or left open on purpose. Run the extraction procedure in the next sections against the live release notes. It takes about twenty minutes and beats any summary, including this one.
A confirmed / inferred / unknown table to fill in against Apple's release notes
| Claim | Status | Where to verify |
|---|---|---|
| Apple ships STP builds as a separate app, alongside stable Safari | Confirmed — longstanding Apple documentation | Apple's STP page |
| Apple publishes per-build release notes grouped by technology area | Confirmed | STP release notes index |
| TP 256 exists and compares against TP 255 in public write-ups | Confirmed — shattered.io, dated 2026-11-05 | Linked write-up |
| TP builds ship on a roughly two-week cadence | Inferred from historical practice, not a stated commitment | Release-note index timestamps |
| Feature X in TP 256 is on by default | Unknown here | The release note for that item |
| Feature X will ship in a specific stable Safari version | Unknown, and not knowable from a TP note | Nothing — see the lag section |
| Any TP 256 item has been reverted on trunk | Unknown here | WebKit commit log |
Copy that table into your own notes and fill the empty cells from the primary source. The act of filling it is the review.
How Safari Technology Preview Relates to Stable Safari: Two Release Tracks, One Engine
The two-track release model — stable Safari versioned releases versus STP builds that track WebKit trunk
Stable Safari ships as a versioned browser tied to Apple's OS cadence, with occasional Safari-only updates for older macOS versions. It is a curated, tested, frozen artifact. Its feature set moves on Apple's timetable, not yours.
Safari Technology Preview is a separate app you install once and update through Software Update. It tracks WebKit trunk far more closely than any Safari release, and it exists so that people who want to test against near-trunk WebKit can do that without waiting for a version bump. In practice, it is a rolling window into an engine under active development.
The two tracks share an engine and a UA family. They do not share guarantees.
Why a feature appearing in STP is not a stability or timeline commitment
An item in a TP release note means one thing: at the time that build was cut, some code implementing that behavior was on trunk. It does not mean the implementation is complete, the specification has settled, the behavior is on by default, or that it will survive review.
Trunk is a working surface. Code lands, gets relanded, gets reverted when an implementer loses an argument with another engine's team. Build a feature-detection shim around an STP-only behavior in November and you may be maintaining it for a shape that does not exist by March.
Pref-gated versus on-by-default: how to tell which one you are looking at from the release notes
This distinction is the highest-value thing you can pull out of a TP note, and it is usually right there in the wording:
- "Enabled by default" — live for anyone running that build. This affects existing pages with no code change at all.
- "Behind a preference", "flagged", or a named
WebKit…developer setting — opt-in. It cannot regress a production user, because no production user has it on. - A bare "Added support for X" is ambiguous. It usually means on by default in that build, but confirm against the WebKit commit or the Web Inspector's experimental-features list.
That third case is where teams get burned.
The honest lag question — what determines how long a TP feature takes to reach stable, and what evidence would confirm a ship date
There is no formula. What actually determines the lag:
- Specification stability. A feature whose spec is still moving gets held, or shipped behind a flag, until it settles.
- Interoperability. Engines coordinate informally. Shipping a divergent implementation is a cost Apple generally avoids.
- Test coverage. Web Platform Tests going green across engines is a strong practical signal; see the WPT results dashboard.
- Release train timing. Even a finished feature waits for the next stable Safari train.
Evidence that would justify a ship-date prediction, in descending order of strength: an entry in Apple's own Safari release notes saying it shipped; a spec at Candidate Recommendation with multi-engine implementation reports; a Chromium or Firefox intent-to-ship thread naming the same behavior. Absent those, any date you write down is a guess wearing a tie. Do not put it in a planning doc.
Building the TP 255 → 256 Delta List Yourself in Twenty Minutes
Pull the two adjacent release-note pages and diff them rather than trusting a summary
The procedure is deliberately dumb:
- Open the STP release notes index and find the entries for build 255 and build 256.
- Save each page's HTML locally as
snapshots/tp-255.htmlandsnapshots/tp-256.html. - Diff them mechanically, then read the diff.
A summary tells you what the summariser thought mattered. A mechanical diff tells you what changed — including the single line about selector parsing that nobody put in the summary and that will break your stylesheet.
Cross-check each item against WebKit commit logs, MDN browser-compat-data, and the WPT results dashboard
For every added item, three independent checks:
- WebKit commit log — search the WebKit repository for the feature's identifier. A commit that says "reland", or a nearby revert, is a loud signal the implementation is unstable.
- MDN browser-compat-data — what other engines claim, and whether the data carries a version flag. BCD is imperfect, but it is versioned, reviewable, and better than memory.
- WPT dashboard — whether the same behavior passes in Chromium and Firefox, which is the closest thing to an interop stamp you will get before release.
If an item is new in TP 256, untested in WPT, and absent from BCD, it is a research item, not an adoption candidate.
A small Node script that normalises and diffs two release-note snapshots into a checklist
// usage: node tools/tp-delta.mjs snapshots/tp-255.html snapshots/tp-256.html
const [oldPath, newPath] = process.argv.slice(2);
if (!oldPath || !newPath) {
console.error("usage: node tools/tp-delta.mjs <old.html> <new.html>");
process.exit(2);
}
const decode = (s) =>
s.replace(/ /g, " ")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, '"')
.replace(/�?39;/g, "'");
const text = (s) => decode(s.replace(/<[^>]*>/g, " ")).replace(/\s+/g, " ").trim();
function extract(html) {
const out = [];
let section = "(unheaded)";
const token = /<h([1-4])[^>]*>([\s\S]*?)<\/h\1>|<li[^>]*>([\s\S]*?)<\/li>/gi;
for (const m of html.matchAll(token)) {
if (m[1]) section = text(m[2]);
else if (m[3]) out.push({ section, text: text(m[3]) });
}
return out;
}
const rules = [
{ kind: "behavior", test: /enabled by default|now on by default|changed|no longer/i },
{ kind: "behavior", test: /preference|behind a flag|opt-in/i },
{ kind: "vendor", test: /-webkit-|safari-only/i },
{ kind: "fix", test: /^(fixed|crash|regression)/i },
];
const classify = (t) => rules.find((r) => r.test.test(t))?.kind ?? "feature";
const id = (i) => i.section + "\u0000" + i.text;
const before = extract(await readFile(oldPath, "utf8"));
const after = extract(await readFile(newPath, "utf8"));
const seen = new Set(before.map(id));
const added = after.filter((i) => !seen.has(id(i)));
const kept = after.length - added.length;
console.log("kept: " + kept + ", added: " + added.length + ", removed: " + (before.length - kept) + "\n");
console.log("| kind | section | item |");
console.log("| --- | --- | --- |");
for (const r of added.sort((x, y) => classify(x.text).localeCompare(classify(y.text))))
console.log("| " + classify(r.text) + " | " + r.section + " | " + r.text + " |");The <li> regex will not survive nested lists, and the heading walk assumes headings precede their lists. Both are true of Apple's release-note pages in my experience, and both are cheap to patch if they change.
Reading the output: separating new features, behavior changes, and bug fixes, because only the middle category silently breaks existing pages
To show the output shape without pretending to know the TP 256 contents, here is the script run against two fixture files with deliberately fake entries:
node tools/tp-delta.mjs fixtures/tp-demo-255.html fixtures/tp-demo-256.html
kept: 2, added: 2, removed: 0
| kind | section | item |
| --- | --- | --- |
| behavior | CSS | Enabled by default: example-prop now applies to grid items. |
| feature | JavaScript | Added support for example-api on all elements. |
Read the kind column first, and read it pessimistically:
feature— new surface. Ignorable. If you do not call it, it cannot hurt you.fix— a bug went away. Sometimes that unblocks you; occasionally it changes a rendering you had compensated for.behavior— the dangerous row. Something that already exists now behaves differently. Your code did not change. Your users' rendering did.
Sort the behavior rows to the top of your review and read every one.
Four Buckets, Not Two — Classifying Every Change in the Delta
The usual split is "shipping in all engines" versus "Safari-only". That is too coarse: a feature can be spec-aligned and still a bad adoption decision, and a behavior change can be spec-aligned and still break you today.
Bucket 1 — spec-aligned and already shipping in Chromium and Firefox (safe to adopt behind detection)
The only bucket where adoption is routine. Settled spec, two independent implementations, and — critically — an existing cross-engine test story. Adopt behind detection anyway, because detection is what protects you from partial implementations in older versions.
Bucket 2 — spec-aligned but Safari-first (interop risk runs the other direction: you become the only engine-dependent code path)
Spec-aligned, Safari-only today. The risk is not that Safari breaks — it is that you build a code path that only runs on one engine, so it only gets exercised by Safari users. Bugs in it are invisible to your Chromium-heavy test suite and your Chromium-heavy team. If you adopt from this bucket, the negative test — the fallback path — matters more than the happy path.
Bucket 3 — vendor-prefixed or Safari-only surface (usable only with a hard fallback)
-webkit--prefixed properties and Safari-only APIs. Legitimate for progressive enhancement, illegitimate as a dependency. The rule is mechanical: if the prefixed declaration is the only thing standing between the user and a broken layout, you have a bug.
Bucket 4 — behavior changes to existing features: selector parsing, layout, storage semantics, media handling; the bucket that causes regressions without any code change
The one that generates incidents. The shapes to watch: a selector previously dropped as invalid now parses and matches; a layout engine fixes an over-constrained case your compensation hack depended on; a storage API changes its eviction order; a media element changes when metadata loads.
None of these require you to write code. All of them can change what a stable Safari user sees — depending on whether the corresponding stable release includes the change. That is why the pref-state reading matters so much: an on-by-default behavior change in TP is a future stable-Safari behavior change, and you want lead time on it.
A table mapping bucket to detection strategy, test strategy, and ship/no-ship decision
| Bucket | Detection strategy | Test strategy | Ship decision |
|---|---|---|---|
| 1. Spec-aligned, multi-engine | Behavior probe; fallback path retained | Positive + negative assertion in all four CI projects | Ship |
| 2. Spec-aligned, Safari-first | Behavior probe plus an engine check, because the fallback is the default path elsewhere | Fallback path gets its own snapshot/visual test; log which path ran | Ship only if the fallback is fully tested |
| 3. Vendor-prefixed / Safari-only | Probe the unprefixed property, not the prefixed one | Stable WebKit project must pass with the feature off | Ship as enhancement, never as dependency |
| 4. Behavior change (on by default) | No detection possible — the behavior is already there | Regression specs pinned to computed values and layout boxes; run on every PR | Do not ship new code assuming the old behavior; audit for reliance |
Feature Detection That Survives Partial WebKit Implementations
Detect behavior, not the presence of a name — construct the thing and assert a computed result
// lib/probe.js
const cache = new Map();
function once(id, check) {
if (!cache.has(id)) {
let passed = false;
try { passed = check() === true; } catch { passed = false; }
cache.set(id, passed);
}
return cache.get(id);
}
export const supportsSelectorHas = () =>
once("selector:has", () => {
const host = document.createElement("div");
host.id = "hackyjs-probe";
host.innerHTML = '<span class="probe-child"></span>';
document.body.append(host);
try {
const hit = document.querySelector("#hackyjs-probe:has(.probe-child)") === host;
host.firstElementChild.remove();
const miss = document.querySelector("#hackyjs-probe:has(.probe-child)") === null;
return hit && miss; // both directions, or it is not usable
} finally {
host.remove();
}
});
export function computed(decl, prop, expected) {
const el = document.createElement("div");
Object.assign(el.style, { position: "absolute", visibility: "hidden" }, decl);
document.body.append(el);
try {
const got = getComputedStyle(el).getPropertyValue(prop).trim();
return got === expected;
} finally {
el.remove();
}
}
Note the negative assertion inside supportsSelectorHas. A selector engine that matches too eagerly passes the naive version of this check and fails the real one.
Why 'x' in element.style, CSS.supports(), and constructor existence all return true for half-built features
They are all surface checks. They answer "does the parser accept this token?" — not "does the engine do the right thing with it?".
A property can be parsed and stored in the CSSOM while the layout code behind it is incomplete or pref-gated. CSS.supports("container-type", "inline-size") returning true and the computed readback disagreeing is not a contradiction; it is two layers of the engine answering two different questions.
Illustrative shape of the failure — reproduce on the specific build you care about rather than trusting my numbers:
probe result
selector:has true
container-type readback false ← CSS.supports() said true; computed readback said no
The readback is the one that matters, because it is the layer your users are looking at.
A probe helper that runs a throwaway DOM check and caches the answer
That is once() above, and the caching is not incidental. Probes append and remove nodes, which forces style and layout. In a component that renders hundreds of times, an uncached probe becomes a layout thrash; the Map turns it into a single measurement per page load. Keep probe elements absolutely positioned and visibility: hidden so they cannot affect scroll height or paint.
The trap with UA sniffing in WebKit: STP and stable Safari share a family, so version strings cannot distinguish pref states
Stable Safari and Safari Technology Preview present the same kind of UA string: an engine build and a Safari version, with no channel marker you should branch on. Even where the strings differ, they differ in a way that is meaningless for the actual question — not "is this STP?" but "does this behavior work here?".
The pref state of a given feature is a flag inside the build. No UA string exposes it. Verify this yourself rather than trusting me: print navigator.userAgent in stable Safari and in STP, side by side, in the same session. Whatever the difference is, it will not tell you whether a preview feature is on.
Defining the fallback contract — with the capability off, layout, hit targets, and focus order must still hold
Write this down as an acceptance criterion, not a sentiment. With the feature detected as unavailable:
- Layout must resolve to the same grid/flex structure, without orphaned gaps or double margins.
- Hit targets must keep their minimum dimensions — buttons that grow when the native feature is present must not shrink to text height when it is absent.
- Focus order must be unchanged. This is the one teams forget: replacing a native element with a fallback often rewrites the tab sequence.
- Text must not overflow its container when the fallback assumes a different font metric.
Then write a test that forces the fallback and asserts all four. A fallback never executed in CI is a fallback that does not work.
The Regressions That STP-Driven Adoption Actually Produces
Adopting a Safari-first API in shared code and shipping it to Firefox and Chromium users
The complaint usually arrives as "the site is broken in Chrome", which makes it sound like a Chrome bug. It is not. It is a code path written against an API only one engine implements, with no detection and no fallback, evaluated eagerly at module scope — so it throws during hydration and takes the whole page with it.
The tell: the failure is total rather than partial. Partial degradation suggests a CSS-level problem; a blank page suggests an unguarded call.
Behavior changes that break existing CSS or JS with no new code at all — how to find these before users do
You find these with pinned assertions, not review. Concretely: a small set of specs that assert computed values and layout boxes for your most load-bearing components, run against every CI browser project. When WebKit changes how a selector parses or a box resolves, one of those assertions flips.
// tests/contracts/layout.spec.ts
test("card grid keeps two columns at 1024px", async ({ page }) => {
await page.setViewportSize({ width: 1024, height: 800 });
await page.goto("/fixtures/card-grid");
const boxes = await page.locator(".card").evaluateAll((els) =>
els.map((el) => Math.round(el.getBoundingClientRect().width)),
);
expect(new Set(boxes).size).toBe(1);
expect(boxes[0]).toBeGreaterThan(300);
});
Unglamorous, and the only thing that reliably catches bucket-4 changes. A visual snapshot works too, but a numeric assertion gives you a diff you can read in a terminal.
Polyfill collisions: a polyfill that predates the native implementation and now fights it
The classic: you ship a polyfill for a feature that did not exist. Years later the engine implements it natively, the polyfill's detection is too loose, and both run. Symptoms range from harmless double-work to duplicated DOM nodes and double-fired event listeners.
Rule: every polyfill must be detected out by a behavior probe, and every polyfill needs a test that runs with the native implementation present. Polyfills are the one place a false positive in detection is guaranteed, because the polyfill itself muddies the surface it is checking.
False confidence from a Chromium-only test suite, and what WebKit-only failures look like in practice
A Chromium-only suite tells you about Chromium. WebKit-only failures tend to be specific and unhelpful-looking:
- Live-region announcements that fire in one order in Chromium and another in WebKit.
- A
position: stickyelement that stops sticking because an ancestor gained a containment context. - A form control whose hit target collapses because of a default padding difference.
- Scroll-driven effects that desynchronise by a frame.
None of these look like "browser support" problems in a bug tracker. They look like UI bugs, and they get triaged as such.
A CI Strategy That Covers WebKit Properly: Four Blocking Projects Plus an STP Lane
Baseline matrix: Chromium, Firefox, stable WebKit, and a mobile WebKit profile on every pull request
Four projects, all blocking. Chromium and Firefox for the ordinary majority case, WebKit because it is the engine most likely to disagree with your assumptions, and a mobile WebKit profile because iOS is where WebKit-only behavior actually reaches users at scale.
A separate non-blocking lane for Safari Technology Preview so trunk churn does not redden main
The STP lane's job is early warning, not gating. If it blocks merges it will be disabled within a month — trunk moves, features land half-finished, and a blocking lane on an unstable target teaches people to ignore red. Non-blocking plus scheduled triage is the only version that survives.
Config sketch for adding an STP-driven project alongside the stable WebKit project
First, an important constraint: Playwright's webkit is its own patched WebKit build, pinned to a Playwright release — not Safari, not STP. You cannot point it at Safari Technology Preview.app and get Safari semantics. What you get is a specific engine snapshot, which is still useful for catching trunk-adjacent WebKit behavior.
// playwright.config.ts
export default defineConfig({
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
{ name: "webkit-mobile", use: { ...devices["iPhone 15"] } },
{
name: "webkit-stp",
use: { ...devices["Desktop Safari"] },
grep: /@tp-watch/,
retries: 0,
},
],
});
For actual STP coverage you need a second mechanism: WebDriver against the STP app. Check whether the bundle ships its own safaridriver, because that determines whether this is automatable at all.
ls "/Applications/Safari Technology Preview.app/Contents/MacOS/"
If safaridriver appears in that listing, the lane is real:
safaridriver --enable
"/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver" -p 4444 &
Then drive it with any WebDriver client, tagging a small subset of specs. If safaridriver is not in the listing, accept that STP coverage is manual and stop pretending otherwise in your CI documentation.
Quarantine rules — how long an STP-only failure may stay red before it is either filed upstream or promoted to blocking
Concrete, so it cannot be reinterpreted:
- An STP-only failure gets the
tp-quarantinelabel and a linked issue within one business day. - It may stay quarantined for 15 calendar days or one Playwright/WebKit upgrade, whichever comes first.
- Within that window one of three things must happen: a WebKit bug filed and linked, the spec amended to explain the failure, or the failure reproduced in stable WebKit — in which case it is promoted to blocking immediately and the quarantine is over.
- At expiry with no resolution, the test is deleted along with a note explaining what coverage was lost. A permanently quarantined test is a lie in a dashboard.
Cost control: run the STP lane on a tagged subset of specs plus every test touching the delta list
Two selectors, unioned: specs tagged @tp-watch, and every spec whose file path appears in the current delta list. Generate the second set from the same script output — a spec touching a bucket-4 item is worth running against trunk even if it is not tagged.
That keeps the lane to a few minutes, which is the difference between a lane that runs on every PR and one that runs weekly and is ignored.
Rollout Sequence: From Detection to Shipping
Step 1 — grep the codebase for anything in buckets 2 through 4
grep -rnE "(-webkit-[a-z-]+|:has\(|@container|showPicker|requestIdleCallback)" \
src/ --include="*.css" --include="*.ts" --include="*.tsx" | head -50
Expect two categories of hit: deliberate prefixed declarations (fine, they have fallbacks) and accidental ones (not fine). Triage into the four buckets and record the counts. The bucket-4 count tells you how exposed you are to behavior changes you did not write.
Step 2 — gate adoption behind detection plus a real fallback, and add the negative test
Every adoption produces two tests: the feature-on path and the feature-off path. Force the off path by stubbing the probe:
// in the fallback test
await page.addInitScript(() => {
window.__HACKYJS_PROBE_FORCE_OFF__ = true;
});
Then have once() honour that flag and return false for every probe. If the page does not render correctly with every capability forced off, the fallback is not real.
Step 3 — enable behind a flag, instrument errors and long tasks, and watch WebKit-specific signal
Ship behind a flag and collect, segmented by engine:
- Uncaught errors and unhandled rejections, grouped by message.
- Layout shift after the feature activates.
- Long tasks over 50ms during activation.
- Which code path ran (native versus fallback), as a custom dimension.
The path dimension is what turns "Safari users report jank" into "0.4% of WebKit sessions on the native path have a task over 200ms".
Step 4 — flip when a second engine ships the same behavior; not when Safari alone does
The rule from the introduction, and the whole article compressed. A second engine shipping the same behavior means the spec has survived contact with two independent implementers and that your code path has a second set of users exercising it. Until then, the feature stays behind detection.
Observed results: what the detection probe and CI lanes actually output, shown alongside the claim rather than asserted
The mechanically verifiable artefact in this workflow is the delta script's own output, which is deterministic given two snapshot files — that is what the fixture run above demonstrates. Probe outputs are build-dependent and must be captured on the build you are actually targeting:
node probe-run.mjs > probe-$(date +%F).txt && git diff --no-index probe-previous.txt probe-$(date +%F).txt
Keep that history. A probe that flips from true to false between two STP builds is the cheapest possible warning that a behavior is being reworked — and it is a warning no release-note summary will give you.
What Would Change This Advice
Signals that a TP feature is heading for stable (spec stabilisation, WPT green, other engines' intent-to-ship)
If you see all three, the conservative rule becomes over-conservative and you should start adopting:
- The relevant specification reaches Candidate Recommendation or equivalent stability.
- WPT results go green for that feature area across Chromium, Firefox, and WebKit on the dashboard.
- Another engine publishes an intent-to-ship, or ships the behavior.
Any one alone is a weak signal. All three together is the industry telling you the shape is settled.
Signals it may be reverted or reworked (pref churn, spec disagreement, repeated reland/revert in the commit log)
- The pref name changes between builds, or the feature moves from on-by-default back to pref-gated. That is a rework in progress.
- The spec issue tracker shows unresolved disagreement about the behavior, not editorial bikeshedding.
- The commit log shows the same change relanded twice with reverts in between. That implementation is not stable, and shipping against it is a coin flip.
What remains unknown and what specific evidence would resolve it
| Open question | Evidence that resolves it |
|---|---|
| The itemised TP 255 → 256 diff | The two release-note pages, diffed with the script above |
| Pref state of any given item | The wording of its release note, plus the Web Inspector experimental features list in STP |
| Whether TP 256 items are already in stable Safari | Apple's Safari release notes for the current version |
| Whether any item is reverted on trunk | The WebKit commit log for that identifier |
| Ship timing | Nothing available today; treat as unknowable |
Further Reading
- Safari Technology Preview release notes — primary source, per-build change lists. The page the delta script consumes.
- WebKit blog — primary source, feature write-ups with implementation context and dates.
- WebKit commit log — primary source, tells you whether a change was relanded or reverted.
- MDN browser-compat-data — primary compatibility dataset, versioned and reviewable per feature.
- Web Platform Tests dashboard — primary interop evidence, per-engine pass/fail per test.
- caniuse — aggregator, useful for a quick read, not an authority on semantics or pref state.
- "Safari Technology Preview 256: What Changed Since 255", shattered.io, 5 November 2026 — secondary context, a community catalog. Useful as a starting index; verify every entry against Apple's notes.
Conclusion
Safari Technology Preview is an early-warning system. It tells you what WebKit's team is working on, roughly, weeks before stable Safari users see any of it. That is genuinely valuable — the only way to get lead time on a bucket-4 behavior change that would otherwise arrive as a bug report.
What it is not is a support matrix entry. A TP build is not a commitment that a behavior will ship, not a statement that it is on by default, and not a version you can meaningfully target. The moment a release note becomes an adoption decision, you have traded a warning system for a liability.
The durable practice is small: run the delta diff every release, classify into four buckets, gate everything from buckets 2 through 4 behind behavior-based detection with a tested fallback, and keep a WebKit lane in CI — blocking for stable, non-blocking for trunk. That is maybe a day of work, and it replaces a category of incident with a category of dashboard.
I am not going to tell you when any TP 256 feature reaches stable Safari. Nobody can, from a TP release note. What I can tell you is that the team that builds the delta list in November is the team that is not surprised in March.
More posts

Why TypeScript 7's VS Code Benchmark Won't Predict Your CI Build Times
A deep dive into TypeScript 7's 11.9x VS Code build benchmark, explaining why synthetic compiler wins don't directly translate to CI build times and what migration risks and editor feedback changes to watch.

Testing Chrome 157's LanguageDetector API on Android Without a Backend
A hands-on walkthrough of Chrome 157's on-device LanguageDetector API on Android, covering feature detection, privacy and latency tradeoffs, fallback strategies, and browser-support caveats for backend-free web apps.

Break-Even Math for Self-Hosted GLM-5.1: GPU Utilization, Ops Overhead, and Claude API Rates
This deep technical walkthrough calculates the break-even point for self-hosting GLM-5.1 versus Claude API rates by modeling GPU utilization, hardware costs, and operations overhead.
Loading comments…