Skip to content

Sync subrepo branch brightdigit-com-260717 - #1

Merged
leogdion merged 23 commits into
mainfrom
brightdigit-com-260717
Jul 27, 2026
Merged

Sync subrepo branch brightdigit-com-260717#1
leogdion merged 23 commits into
mainfrom
brightdigit-com-260717

Conversation

@leogdion

Copy link
Copy Markdown
Member

Draft PR establishing the initial package content for this split-off subrepo.

The subrepo branch was pushed from the brightdigit.com monorepo via git subrepo push; main was tied in with a --allow-unrelated-histories merge so this diff is reviewable.

🤖 Generated with Claude Code

leogdion and others added 4 commits July 14, 2026 21:19
Medium:
- TailwindKit: fix negative spacing emitting invalid `--N` classes for every
  utility except margins. Route top/right/bottom/left/inset, gap(X/Y), and
  space(X/Y) through a shared DefaultSpacing.className(prefix:) that moves the
  `-` ahead of the prefix. Add negative-spacing coverage.
- import buttondown: make repeated imports idempotent. Filter emails to the
  genuinely-new ones before numbering, keyed on explicit subject number OR slug
  (existingSlugs), so unnumbered sent emails are no longer re-imported under a
  fresh number each run. Add existingSlugs(in:) reader + regression test.
- CI: add ContributeButtondown (orphaned test target) and the other split
  packages to .github/packages.json so their tests/builds run again.

Low:
- Restore raw <img> (no alt) across the Post/Podcast/Newsletter cards for exact
  markup parity; Plot's Image(_:) was injecting alt="".
- Remove the invalid bare `gap` TailwindKit utility.
- existingIssueNumbers: distinguish a missing directory (silent) from a real
  read failure (logged) instead of swallowing both via try?.
- Align split-package macOS floor to .v15 (matches root/Publish).
- Bump Styling/.nvmrc to Node 20 and declare the engines floor for Tailwind v4.
- Correct the reconcile existingStatus doc to match the captured-at-planning
  write guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	.gitignore
#	Package.swift
#	README.md
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 112 files, which is 12 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e5c87953-3eb7-480b-886f-90500050e708

📥 Commits

Reviewing files that changed from the base of the PR and between 2150548 and 4d1465c.

⛔ Files ignored due to path filters (3)
  • Sources/TailwindKit/TailwindKit.docc/Resources/TailwindKitLogo.png is excluded by !**/*.png
  • Sources/TailwindKit/TailwindKit.docc/Resources/TailwindKitLogo.svg is excluded by !**/*.svg
  • Sources/TailwindKit/TailwindKit.docc/Resources/TailwindKitLogo@2x.png is excluded by !**/*.png
📒 Files selected for processing (126)
  • .claude/agent-notes.md
  • .claude/skills/claude-handoff/SKILL.md
  • .claude/skills/claude-handoff/agents/openai.yaml
  • .claude/skills/grill-me/SKILL.md
  • .claude/skills/grill-me/agents/openai.yaml
  • .claude/skills/grill-with-docs/SKILL.md
  • .claude/skills/grill-with-docs/agents/openai.yaml
  • .claude/skills/grilling/SKILL.md
  • .claude/skills/grilling/agents/openai.yaml
  • .claude/skills/handoff/SKILL.md
  • .claude/skills/handoff/agents/openai.yaml
  • .claude/skills/loop-me/SKILL.md
  • .claude/skills/loop-me/agents/openai.yaml
  • .claude/skills/research/SKILL.md
  • .claude/skills/research/agents/openai.yaml
  • .claude/skills/to-spec/SKILL.md
  • .claude/skills/to-spec/agents/openai.yaml
  • .claude/skills/to-tickets/SKILL.md
  • .claude/skills/to-tickets/agents/openai.yaml
  • .claude/skills/writing-great-skills/GLOSSARY.md
  • .claude/skills/writing-great-skills/SKILL.md
  • .claude/skills/writing-great-skills/agents/openai.yaml
  • .devcontainer/devcontainer-lock.json
  • .devcontainer/devcontainer.json
  • .github/actions/setup-tools/action.yml
  • .github/dependabot.yml
  • .github/workflows/DangerPR.yml
  • .github/workflows/TailwindKit.yml
  • .github/workflows/TailwindKitTest.yml
  • .github/workflows/check-unsafe-flags.yml
  • .github/workflows/claude-code-review.yml
  • .github/workflows/claude.yml
  • .github/workflows/cleanup-caches.yml
  • .github/workflows/swift-source-compat.yml
  • .gitignore
  • .mise.toml
  • .periphery.yml
  • .spi.yml
  • .swift-format
  • .swift-version
  • .swiftlint.yml
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • CONTRIBUTING/Changelog_Guidelines.md
  • CONTRIBUTING/Contributing.md
  • Dangerfile.swift
  • LICENSE
  • Package.swift
  • README.md
  • RELEASE_NOTES.md
  • Scripts/header.sh
  • Scripts/lint.sh
  • Sources/TailwindKit/Core/DefaultTailwindClass.swift
  • Sources/TailwindKit/Core/DefaultVariant.swift
  • Sources/TailwindKit/Core/String+ArbitraryValue.swift
  • Sources/TailwindKit/Core/TW.swift
  • Sources/TailwindKit/Core/TailwindClass.swift
  • Sources/TailwindKit/Core/TailwindStyle.swift
  • Sources/TailwindKit/Core/TailwindStyleBuilder.swift
  • Sources/TailwindKit/Core/TailwindToken.swift
  • Sources/TailwindKit/Core/Variant.swift
  • Sources/TailwindKit/Flexbox.swift
  • Sources/TailwindKit/Integration/TailwindClassAttribute.swift
  • Sources/TailwindKit/Layout/AspectRatio.swift
  • Sources/TailwindKit/Layout/Display.swift
  • Sources/TailwindKit/Shared/Breakpoints.swift
  • Sources/TailwindKit/Styling/ArbitraryStyling.swift
  • Sources/TailwindKit/Styling/BorderStyling.swift
  • Sources/TailwindKit/Styling/ColorStyling.swift
  • Sources/TailwindKit/Styling/DisplayStyling.swift
  • Sources/TailwindKit/Styling/EffectsStyling.swift
  • Sources/TailwindKit/Styling/FlexGridStyling.swift
  • Sources/TailwindKit/Styling/ListStyling.swift
  • Sources/TailwindKit/Styling/PositioningStyling.swift
  • Sources/TailwindKit/Styling/SizingStyling.swift
  • Sources/TailwindKit/Styling/SpacingStyling.swift
  • Sources/TailwindKit/Styling/TailwindStyleBuilder+FlexGridStatic.swift
  • Sources/TailwindKit/Styling/TransitionStyling.swift
  • Sources/TailwindKit/Styling/TypographyStyling.swift
  • Sources/TailwindKit/Styling/VariantStyling.swift
  • Sources/TailwindKit/TailwindKit.docc/TailwindKit.md
  • Sources/TailwindKit/TailwindKit.swift
  • Sources/TailwindKit/Tokens/Closed/Align.swift
  • Sources/TailwindKit/Tokens/Closed/BorderSide.swift
  • Sources/TailwindKit/Tokens/Closed/Flex.swift
  • Sources/TailwindKit/Tokens/Closed/FlexDirection.swift
  • Sources/TailwindKit/Tokens/Closed/Justify.swift
  • Sources/TailwindKit/Tokens/Closed/ListStyle.swift
  • Sources/TailwindKit/Tokens/Closed/ObjectFit.swift
  • Sources/TailwindKit/Tokens/Closed/Position.swift
  • Sources/TailwindKit/Tokens/Closed/Shade.swift
  • Sources/TailwindKit/Tokens/Closed/TextAlign.swift
  • Sources/TailwindKit/Tokens/Closed/VerticalAlign.swift
  • Sources/TailwindKit/Tokens/Extensible/Color.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultColor.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultDropShadow.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultEase.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultFontWeight.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultMaxWidth.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultRadius.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultShadow.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultSize.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultSpacing.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultTextSize.swift
  • Sources/TailwindKit/Tokens/Extensible/DefaultTracking.swift
  • Sources/TailwindKit/Tokens/Extensible/DropShadow.swift
  • Sources/TailwindKit/Tokens/Extensible/Ease.swift
  • Sources/TailwindKit/Tokens/Extensible/FontWeight.swift
  • Sources/TailwindKit/Tokens/Extensible/MaxWidth.swift
  • Sources/TailwindKit/Tokens/Extensible/Radius.swift
  • Sources/TailwindKit/Tokens/Extensible/Shadow.swift
  • Sources/TailwindKit/Tokens/Extensible/Size.swift
  • Sources/TailwindKit/Tokens/Extensible/Spacing.swift
  • Sources/TailwindKit/Tokens/Extensible/TextSize.swift
  • Sources/TailwindKit/Tokens/Extensible/Tracking.swift
  • Tests/TailwindKitTests/AspectRatioTests.swift
  • Tests/TailwindKitTests/BreakpointTests.swift
  • Tests/TailwindKitTests/FlexboxTests.swift
  • Tests/TailwindKitTests/TailwindClassAttributeTests.swift
  • Tests/TailwindKitTests/TailwindStyleCoverageTests.swift
  • Tests/TailwindKitTests/TailwindStyleExtensibilityTests.swift
  • Tests/TailwindKitTests/TailwindStyleSetsAndCustomTests.swift
  • Tests/TailwindKitTests/TailwindStyleTests.swift
  • codecov.yml
  • docs/tailwind-v4-value-model.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brightdigit-com-260717

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

leogdion and others added 2 commits July 17, 2026 20:34
Keep monorepo path deps in Package.swift; rewrite to revision-pinned URLs
in standalone CI via Scripts/ensure-remote-deps.sh.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

leogdion and others added 13 commits July 18, 2026 08:52
Plot's working branch is gone; Package.swift already uses url+branch so the dual-mode rewrite script is obsolete.

Co-authored-by: Cursor <cursoragent@cursor.com>
Wave 0 release PRs are merged to main, so the v1.0.0 branches are stale
leftovers rather than the consumer pin. Point Wave 0 dependencies at
`branch: "main"`.

Wave 1/2 dependencies stay on their brightdigit-com-* working branches,
which are not yet merged to main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Wave 0 repin left the committed lockfile stale, which hard-fails CI
that runs with automatic resolution disabled ("an out-of-date resolved
file was detected"). Regenerate it so the lockfile matches the manifest.

These packages have no local `path:` dependencies left, so the
skip-package-resolved workflow flag is no longer needed — the committed
Package.resolved is now the real lockfile and should be honored by CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TailwindKit shipped without a license file. Add the standard BrightDigit MIT
license so the README License badge and SPI license detection resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DangerPR.yml and TailwindKitTest.yml both run on macos-12, a retired runner
label, so their jobs queue forever and never report — they are the two
non-successful checks on PR #1. The package CI in TailwindKit.yml already
covers build and test on every platform, so delete both workflows along with
the Dangerfile they drove.

Also drop the abandoned CHANGELOG.md and CONTRIBUTING/ directory (last touched
in 2022, superseded by RELEASE_NOTES.md and the shared BrightDigit
contribution process), and re-sync .swiftlint.yml with the donor now that the
Dangerfile.swift exclusion is moot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- fail-fast: true on all four matrix legs (ubuntu, windows, macOS platforms,
  android) so a broken leg stops the run instead of burning the whole matrix.
- Ubuntu coverage step moves off the SHA-pinned brightdigit fork of
  swift-coverage-action back to sersoft-gmbh/swift-coverage-action@v5, and drops
  fail-on-empty-output.
- Drop verbose: true from the Codecov upload.
- build-macos-platforms: remove the ENABLE_WATCHOS step gate and add the
  visionOS row.
- Re-sync claude.yml with the template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Vendor .claude/agent-notes.md and the 10 shared .claude/skills/ directories as
  real directories.
- Rename CLAUDE.md to AGENTS.md as the canonical agent instruction file, leave
  CLAUDE.md as a symlink to it, and append the Memory & Corrections Convention
  section pointing at .claude/agent-notes.md.
- Add .spi.yml so Swift Package Index builds documentation for the TailwindKit
  target on Swift 6.4.
- codecov.yml ignore pattern -> Tests/**/* so nested test sources are matched.
- Scripts/lint.sh: drop the ButtondownKit-specific Generated/ file enumeration
  (TailwindKit has no generated sources) in favour of --recursive Sources Tests,
  and force periphery onto the native build system so it finds the index store
  under Swift 6.4. The header.sh -p argument stays TailwindKit.
- Add RELEASE_NOTES.md describing what this branch changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New TailwindKit.docc catalog with a real landing page: overview, the
  capability-protocol and token model, variants, arbitrary values, the Plot
  bridge, and a Topics tree covering every public symbol.
- Placeholder TailwindKitLogo.png in the catalog Resources, referenced from the
  README header.
- README rewritten on the standard BrightDigit structure: logo, the eight-badge
  block, tagline, What is TailwindKit?, Installation, Usage, Testing,
  Requirements and License. Requirements now state Swift 6.4 / macOS 13 /
  Ubuntu Noble to match Package.swift and CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove comments describing behavior the workflow no longer has: the coverage
step is not a SHA-pinned brightdigit fork (it is sersoft-gmbh@v5), and the
watchOS leg is not gated on an ENABLE_WATCHOS repo variable. Comment-only;
no step, action, or matrix row changes.

Mirrors brightdigit/Contribute#18 in the donor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread README.md Outdated
@@ -1,11 +1,158 @@
![TailwindKit Logo](Sources/TailwindKit/TailwindKit.docc/Resources/TailwindKitLogo.png)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the logo

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b25bd3d, using the SVG you sent.

Worth recording why it needed fixing: the committed PNG wasn't a stale or low-res version of the right mark — it was different artwork. A 1200×630 wide gradient banner with a "TailwindKit" wordmark and only the double-wave glyph. Your SVG is a 106×106 square icon in Tailwind's own sky-blue (#38bdf8) with the Swift bird and the wave glyph.

Now ships, rendered from your file with rsvg-convert:

  • TailwindKitLogo.svg — vector source of truth
  • TailwindKitLogo.png — 600×600
  • TailwindKitLogo@2x.png — 1200×1200

matching the .svg + .png + @2x pattern ContributeWordPress uses. The README image path is unchanged, so it picks up the new artwork automatically.

Comment thread Package.swift Outdated
],
dependencies: [
.package(
url: "https://github.com/brightdigit/Plot.git",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we consider removing Plot dependency

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — removed, in b7a2dba. TailwindKit now has zero dependencies, not even Foundation.

I measured it before deciding: Plot was reachable from 1 of 56 source files, all 4 test files were already Plot-free, and the bridge was 3 functions that each reduced to .class(style.rendered). Your call — protocol in TailwindKit, extension in BrightDigit — is what shipped:

public protocol TailwindClassAttribute {
  static func tailwindClass(_ className: String) -> Self
}

extension TailwindClassAttribute {
  public static func tailwind(_ style: TailwindStyleBuilder) -> Self {
    .tailwindClass(style.rendered)
  }
}

BrightDigit supplies the binding in Sources/BrightDigitSite/Nodes/Node+Tailwind.swift — two conditional conformances. Services+Box.swift is byte-identical; bigImage.tailwind(.rounded(.lg)) still compiles and renders the same, verified against the root build and ServicesBoxTests.

I verified leading-dot inference by compiling a probe before committing to this, with a negative control to prove the probe was real. Node.div(.tailwind(.flex.gap(4))) still infers unannotated, nested arbitrarily deep, interleaved with Plot's own .class(…)/.id(…). One conditional conformance covers every Context — no per-context work.

Two things you'll want to know:

1. Removing Plot alone would not have compiled. TailwindKit had no import Foundation anywhere, but four files call String.replacingOccurrences — they were only building because import Plot leaked Foundation in transitively. I added escapingSpaces(_:) (a map over characters) in Core/ArbitraryValue.swift, which is why the package ends at genuinely zero dependencies rather than trading Plot for Foundation. Now covered by tests with real spaces (w-[calc(100%_-_2rem)]).

2. Plot's Component can't use the protocol. Swift rejects retroactively conforming a protocol to another protocol (extension of protocol 'Component' cannot declare inheritance relationship), and Component.class(_:replaceExisting:) returns an existential rather than Self. So Component.tailwind stays a hand-written one-liner on the BrightDigit side. That asymmetry is a language limitation, and it's documented in the protocol so nobody "fixes" it later.

CI is green across Ubuntu, Android, Windows (2022 + 2025), macOS, iOS/tvOS/watchOS/visionOS — the Linux and Windows runs being the ones that actually prove the Foundation removal. 48 tests pass, lint clean (0 violations / 62 files), periphery reports no unused code.

Since it now depends on nothing, TailwindKit could tag alongside Wave 0 rather than Wave 1 — I left it in Wave 1 because nothing needs it earlier, but it's no longer constrained.

leogdion and others added 4 commits July 26, 2026 18:56
The committed PNG was the wrong artwork: a 1200x630 gradient banner with a
wordmark. The actual mark is a square icon — a Swift bird over the Tailwind
double-wave glyph on a #38bdf8 rounded square.

Ships the vector source alongside 600x600 and @2x renders, matching the
.svg + .png + @2x pattern used elsewhere in the family. The README image path
is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TailwindKit models Tailwind utilities; it never needed an HTML library. Plot was
reachable from exactly one of 56 source files, and only to spell
.class(style.rendered).

- Add TailwindClassAttribute: one static requirement, tailwindClass(_:) -> Self,
  with .tailwind(_:) supplied by a protocol extension. Consumers conditionally
  conform their own element type; leading-dot inference resolves through the
  extension, so `Node.div(.tailwind(.flex.gap(4)))` still reads natively.
- Delete Sources/TailwindKit/Plot/ and the Plot dependency.
- Add escapingSpaces(_:) so arbitrary-value escaping no longer needs
  String.replacingOccurrences. Four files were relying on Foundation leaking in
  transitively through Plot, so removing Plot alone would not have compiled.

The package now has no dependencies at all — not even Foundation.

Plot's Component keeps a hand-written one-liner on the consumer side: a protocol
cannot be retroactively conformed to another protocol, and Component.class
returns an existential rather than Self. That asymmetry is a Swift limitation and
is documented in the protocol.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Matches the Contribute-family packages, which all point at the vector asset and
keep the rasters as companions.

The SVG carried only a viewBox, so a consumer rendering it (GitHub included)
would scale it to the full container width. Pinned to a 200x200 presentation
size, the same ~200px height the sibling logos use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two review follow-ups, both API shape only — no rendered output changes.

Rename the `TailwindClassAttribute` requirement from `tailwindClass(_:)` to
`class(_:)`, the factory HTML libraries already declare. Plot's `Node` and
`Attribute` both expose

    public static func `class`(_ className: String) -> Self

under `Context: HTMLContext`, matching on argument label, return type and
constraint — so a consumer's conformance is now a declaration with an empty
body rather than a forwarding one-liner:

    extension Node: TailwindClassAttribute where Context: HTMLContext {}

`Component` still needs its instance-method sugar written directly: it is a
protocol (Swift forbids retroactively conforming a protocol to another) and its
`class` takes an extra parameter and returns an existential, not `Self`.

Move the file-scope `escapingSpaces(_:)` to `String.escapingSpaces` in
`Core/String+ArbitraryValue.swift` — no global functions. The file is renamed
because SwiftLint's `file_name` rule runs at `severity: error`. This also
removes the private shim in `ArbitraryStyling` that existed only to disambiguate
against the global; a `String` member cannot be shadowed that way. The body
stays a `map` over characters, keeping the package free of Foundation.

The stub in `TailwindClassAttributeTests` now declares only `class(_:)` and no
protocol-specific member, so the suite failing to compile is itself the
assertion that conformance stays implementation-free.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@leogdion
leogdion marked this pull request as ready for review July 27, 2026 14:22
@leogdion
leogdion merged commit 20731db into main Jul 27, 2026
16 checks passed
@leogdion
leogdion deleted the brightdigit-com-260717 branch July 27, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant