clsx
Easily define conditional class attributes for HTML elements.
Motivation
Defining conditional class attributes based on a state can be cumbersome and
error-prone with string concatenation. clsx provides a utility function to
handle this logic efficiently, bringing the popular idea from the JavaScript
ecosystem to the Python ecosystem.
Installation
The package is available on PyPI and can be installed using pip.
pip install clsx
Usage
The clsx function takes any number of arguments and returns a string that can
be used as the value of the class attribute of an HTML element.
from clsx import clsx
clsx() # ""
clsx("") # ""
clsx("foo") # "foo"
clsx("foo foo") # "foo"
clsx("foo \n\t foo") # "foo"
clsx("foo", None) # "foo"
clsx("foo", None, "bar") # "foo bar"
clsx("foo", None, "bar", None) # "foo bar"
clsx("foo", None, "bar", None, "baz") # "foo bar baz"
clsx("foo", False) # ""
clsx("foo", True) # "foo"
clsx("foo", "bar") # "foo bar"
clsx("foo", "bar", "baz") # "foo bar baz"
clsx("foo", "bar", "baz", "qux") # "foo bar baz qux"
clsx(["foo", "bar"]) # "foo bar"
clsx(["foo", "bar"], ["baz", "qux"]) # "foo bar baz qux"
clsx([("foo", True), ("foo", "bar", "baz")]) # "foo bar baz"
clsx(["foo foo"], "foo", "bar") # "foo bar"
clsx("foo", ["foo", "bar", "baz"]) # "foo bar baz"
clsx({"foo": True, "bar": False}) # "foo"
clsx({"foo": True, "bar": True}) # "foo bar"
clsx({(lambda: "foo"): True}) # "foo"
clsx(lambda: [lambda: ("foo", True)]) # "foo"
Low-level API
The core of the implementation is the methods in the ExpressionEvaluator
class. For each type of input, there is a corresponding method that evaluates
the input and returns an iterator of class names. These methods are designed to
be chained together to form the final iterable.
For optimal performance and memory consumption, ExpressionEvaluator consists
of several generator methods that yield the class names one by one, as they
are consumed. This allows the evaluation process to be done lazily, which is
especially useful when dealing with large/infinite and/or nested inputs. Since
the evaluation is done lazily, the both the CPU and memory usage are kept to a
minimum.
from clsx.evaluation import ExpressionEvaluator
output_stream = ExpressionEvaluator.evaluate(input_stream)
Modularity
The implementation supports on-the-fly modifications of class names out of the
box by its nature. For more complex use cases similar to deduplication,
CSS Modules resolution or
anything else, the low-level API can be extended by chaining the output
iterable with other generator functions. This allows for a modular design
where each step of the evaluation process can be customized and extended
independently, with zero extra overhead.
For an example of how to extend the low-level API, see the deduplication
section below.
Deduplication
The deduplication is always enabled when using the high-level API. However, when
using the low-level API, the deduplication must be done manually. This can be
achieved by using the dedup function from the clsx.contrib.itertools
module, which is a generator function that deduplicates the input stream without
ahead of planning or buffering. See the example below.
from clsx.contrib.itertools import dedup
from clsx.evaluation import ExpressionEvaluator
output_stream = ExpressionEvaluator.evaluate(input_stream)
deduplicated_output_stream = dedup(output_stream)
CSS Modules
The CSS Modules resolution is not supported out of the box, but can be
achieved by modifying the output stream by chaining it with a custom generator
function that resolves the class names. See the example below.
from clsx.abc import ClassNameIterable
from clsx.evaluation import ExpressionEvaluator
STYLES = {"foo": "Zm9v"}
def resolve_css_module_class_names(
iterable: ClassNameIterable,
) -> ClassNameIterable:
for class_name in iterable:
yield STYLES.get(class_name, class_name)
output_stream = ExpressionEvaluator.evaluate(input_stream)
resolved_output_stream = resolve_css_module_class_names(output_stream)
Inspiration
This project is inspired by the popular idea implemented and widely used in the
JavaScript ecosystem. The name clsx is a positive nod to the project
clsx by @lukeed,
which provides a similar functionality for JavaScript.
The name clsx in this project is used as a shorthand for "class expression"
and it might be pronounced as "classics".
License
This project is licensed under the MIT License.
See the LICENSE file for more information.
Metadata
Release files for clsx 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| clsx-0.1.1.tar.gz | 11.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| clsx-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.3 kB
Release files / clsx-0.1.1.tar.gz
| Download URL | clsx-0.1.1.tar.gz |
|---|---|
| Size | 11.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
99f987d5034c20565d10fec98e8c24d8fca9790ac43e56dea01102bb7f062569
|
|
BLAKE2b-256 checksum How to use checksums |
4f6533d43dafd5b90a0a6d4e82a41ca9820e97938c527e9b20509c3221241fb3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.27.0
|
Release files / clsx-0.1.1-py3-none-any.whl
| Download URL | clsx-0.1.1-py3-none-any.whl |
|---|---|
| Size | 19.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
41582770291757618c86556d9652376d4c7c02db70b5ae0d2ecb330947845f4e
|
|
BLAKE2b-256 checksum How to use checksums |
d3ce01f74fa59ad4ab0f71ee6d1cb764c2e0562dc773220ed59631677cf79c66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.27.0
|
Security for clsx 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.