Skip to content

Add [CSS selector] id/class/attribute setting and (classes=...) for appending to class - #9

Merged
Tronic merged 18 commits into
mainfrom
css-selector-style-attributes
Jul 8, 2026
Merged

Tronic merged 18 commits into
mainfrom
css-selector-style-attributes

Conversation

@ahopkins

@ahopkins ahopkins commented Aug 27, 2023 •

Copy link
Copy Markdown
Member

The goal of this PR is to come up with a nicer API for assigning HTML id and class attributes that clash with Python syntax. One approach is to overload the __getitem__ method to allow for this:

from html5tagger import Document

doc = Document("Test")
doc.div["#some-id.one.two.three[href=/some/path][another='foo bar']"]

print(str(doc))
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test</title>
<div id="some-id" href="/some/path" another="foo bar" class="one two three"></div>

There are a few considerations:

  1. Whether the value passed to __getitem__ should be a single string, or a tuple of strings: ["#id", "class", ...]
  2. Whether the values (particularly class names) should be dot (.) prepended: .one.two.three v one two three
  3. Whether arbitrary attributes should be supported in this pattern

The implementation I proposed answers these as by opting for a single string that follows CSS-style selector. While potentially we could go with #some-id one two three and just check the first character for a #, in which case we pop off the one value, this pattern was not chosen because it then means that this lib would be introducing a new pattern that is not found in HTML.

Alternatively, we could have multiple values builder["#some-id","one two three"]. While this is valid Python, it is a bit awkward (granted, any overload of __getitem__ is certainly non-standard).

Finally, assuming "#some-id.one.two.three" is preferred over "#some-id one two three", the next logical question is why not arbitrary attribute assignment with [foo=bar] style syntax.

As a side benefit, it would allow a data-foo_bar=thing attribute, if that is desired.

@Tronic

Tronic commented Sep 1, 2023 •

Copy link
Copy Markdown
Member

Looks good. Neat how compact & fast implementation you got for this.

Remaining considerations: should we catch/handle if the same attributes are defined as kwargs too (I believe now you get another attribute by the same name in HTML)? Possibly even restructure html5tagger such that the whole opening tag is written at once instead of appending attributes to HTML snippet the way it currently does.

@Tronic

Tronic commented Sep 3, 2023

Copy link
Copy Markdown
Member

Needs README update I guess, but is there anything else stopping this from merging?

@CiberNin

CiberNin commented Feb 5, 2025 •

Copy link
Copy Markdown

data-foo_bar=thing is a requirement for my use case (datastar uses attrs of that style)

Tronic added 5 commits July 6, 2026 05:07
The call operator now accepts an optional `classes` keyword argument.
It accepts a whitespace-separated string or an iterable of class names
and appends them to the existing class attribute on the current tag.
The existing class string is extracted from the tag only when the
parameter is used, so callers who do not use it incur no overhead.
@Tronic

Tronic commented Jul 7, 2026

Copy link
Copy Markdown
Member

data-foo_bar=thing is a requirement for my use case (datastar uses attrs of that style)

Removed mangling from CSS syntax, so this will work as you wanted.

@Tronic
Tronic marked this pull request as ready for review July 7, 2026 19:11
Copilot AI review requested due to automatic review settings July 7, 2026 19:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a CSS-selector-inspired API for setting HTML attributes that are awkward to express as Python keyword arguments (notably id and class), by overloading Builder.__getitem__ to accept selector strings like #id, .class, and [attr=value].

Changes:

  • Add Builder.__getitem__ parsing for #id, .class, and [attr[=value]] selector fragments (including chaining).
  • Add classes= kwarg support to append to an existing class attribute while preserving attribute ordering.
  • Add extensive tests and README documentation for the new selector/classes= behaviors.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
html5tagger/builder.py Implements CSS-selector [] attribute assignment and classes= class-appending behavior.
html5tagger/util.py Refactors attribute value escaping into escape_attr_value() and extends attributes() to optionally skip name mangling.
tests/test_html5tagger.py Adds tests covering selector parsing, attribute emission, chaining, and classes= append semantics.
README.md Documents the new selector-style attribute assignment and classes= usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread html5tagger/util.py
Comment thread html5tagger/builder.py
Comment thread html5tagger/builder.py
Comment thread README.md Outdated
Comment thread tests/test_html5tagger.py
@Tronic
Tronic force-pushed the css-selector-style-attributes branch from 669810e to 7771f69 Compare July 7, 2026 23:29
@Tronic Tronic changed the title Add CSS-style attribute assignment Add [CSS selector] id/class/attribute setting and (classes=...) for appending to class Jul 8, 2026
@Tronic
Tronic merged commit 0d9f097 into main Jul 8, 2026
7 checks passed
@Tronic
Tronic deleted the css-selector-style-attributes branch July 8, 2026 01:09
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.

4 participants