Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(theme): Branded theming via brand_yml #1743

Merged
merged 83 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
f0b3464
feat: initial BrandTheme
gadenbuie Oct 21, 2024
730af5c
feat: rename ThemeBrand
gadenbuie Oct 21, 2024
b73470f
feat: Add `_brand*.yml` to default reload includes
gadenbuie Oct 21, 2024
ef936ea
feat(Theme): Add `from_brand()` method
gadenbuie Oct 22, 2024
9ebf4a5
docs: Add brand-yml to interlinks inventories
gadenbuie Oct 22, 2024
9fe9714
chore: add brand_yml to dev dependencies too
gadenbuie Oct 22, 2024
3943ebc
chore(Theme): Rename method `_html_dependencies`
gadenbuie Oct 22, 2024
669e08a
feat: get fonts dependency from brand.typography
gadenbuie Oct 23, 2024
263ee07
chore: clean up code
gadenbuie Oct 23, 2024
6035788
feat: Apply brand.color.palette to bootstrap color map
gadenbuie Oct 23, 2024
3184691
fix: Use Bootstrap major version
gadenbuie Oct 23, 2024
137cc4c
chore: Add todo comments
gadenbuie Oct 23, 2024
949f6f1
refactor: Move ThemeBrand init code into constructor
gadenbuie Oct 23, 2024
2af0941
feat(ThemeBrand): sanitize color name into valid sass/css variable names
gadenbuie Oct 23, 2024
03adf5c
refactor: Use `brand.color.to_dict()` method
gadenbuie Oct 23, 2024
ced644a
chore: Apply suggestions from code review
gadenbuie Oct 23, 2024
409a53b
refactor: More strongly type sass var maps
gadenbuie Oct 23, 2024
dcf4e32
refactor: Don't version the bootstrap color list
gadenbuie Oct 23, 2024
948ddce
refactor: Consolidate all brand.color sass var logic
gadenbuie Oct 23, 2024
142409e
refactor: Raise ThemeBrandUnmappedFieldError following envvar
gadenbuie Oct 23, 2024
048622e
chore: Update brand.ya?ml reload includes
gadenbuie Oct 23, 2024
96a9b95
Merged origin/main into feat/brand-yml
gadenbuie Oct 23, 2024
fc66b21
feat: Convert `typography.base.size` to `rem` for Bootstrap
gadenbuie Oct 23, 2024
20b1210
feat(brand): Add example app and brand
gadenbuie Oct 23, 2024
b905415
feat(brand): finish mapping the variables
gadenbuie Oct 23, 2024
95ffd5a
chore: Add some notes in example brand
gadenbuie Oct 23, 2024
db42401
chore: add a few more notes
gadenbuie Oct 23, 2024
f785d75
refactor: Simplify splitting css value and unit
gadenbuie Oct 24, 2024
aaed92a
chore: link back to bootstrap source for code rules
gadenbuie Oct 24, 2024
a012b8a
fix(brand): Map typography.link.color to $link-color-dark too
gadenbuie Oct 24, 2024
a9fb968
feat(example): Add colors page
gadenbuie Oct 24, 2024
5624064
fix(typing): of split_css_value_and_unit()
gadenbuie Oct 24, 2024
67e9b7d
feat(brand): Swap foreground/background in dark mode
gadenbuie Oct 24, 2024
c772164
feat(brand): Pick white/black from brand's foreground/background and …
gadenbuie Oct 24, 2024
2573e0a
feat(brand-example): Add color swatch page
gadenbuie Oct 24, 2024
e05ab3d
chore: remove sass debug output
gadenbuie Oct 24, 2024
484c89b
feat(brand): restore card borders in dark mode if brand makes dark mode
gadenbuie Oct 24, 2024
51cf4f1
feat(brand): Mix of font sources
gadenbuie Oct 24, 2024
75ab45d
fix(brand): Rules for code-block-line-height
gadenbuie Oct 24, 2024
792af38
refactor: ._handle_unmapped_variable method
gadenbuie Oct 24, 2024
c5f6302
refactor(ThemeBrand): Separate into smaller methods
gadenbuie Oct 24, 2024
0fe7fa1
chore(brand): rename methods and add sass comments for dividers
gadenbuie Oct 24, 2024
b1d0ccc
refactor: move theme method calls into helper methods
gadenbuie Oct 24, 2024
a4eee03
refactor: factor out get_theme_name
gadenbuie Oct 24, 2024
ab5085e
refactor: Rename BrandBootstrapConfig
gadenbuie Oct 24, 2024
75a7f97
feat: read layers from `brand.defaults.shiny.theme.*`
gadenbuie Oct 24, 2024
438605f
chore: Add `!default` flag
gadenbuie Oct 24, 2024
f083519
chore: early return in no-op case
gadenbuie Oct 24, 2024
dbc4759
refactor: Static method for brand to sass variable helpers
gadenbuie Oct 24, 2024
6e57945
fix: brand-yml inventory objects location
gadenbuie Oct 24, 2024
1c2a504
feat(typography): Map inline code color to $code-color-dark
gadenbuie Oct 24, 2024
9591fb2
example(brand): Keep navbar visible
gadenbuie Oct 24, 2024
721b1be
feat: check that brand_yml is installed before using
gadenbuie Oct 24, 2024
c314806
refactor(BrandBootstrapConfig): Make static methods
gadenbuie Oct 24, 2024
cf103d8
refactor: return brand/bootstrap color sass vars separately
gadenbuie Oct 24, 2024
82b4493
chore: set black/white to brand black white if not set
gadenbuie Oct 24, 2024
59b93c9
refactor: simplify return value
gadenbuie Oct 24, 2024
9e8211c
chore(types): Fix return value
gadenbuie Oct 24, 2024
22bd9bd
refactor: Move preset checking into `ui.Theme()`
gadenbuie Oct 24, 2024
1242c6d
fix: defaults from `defaults.bootstrap`
gadenbuie Oct 24, 2024
9bebe72
fix: rework layer adding to ensure correct order
gadenbuie Oct 24, 2024
0d72daa
chore: update type hint
gadenbuie Oct 24, 2024
fa78936
temp: switch to brand_yml from github
gadenbuie Oct 25, 2024
f7f5605
refactor: brand_yml now handles validation of `color.palette` names
gadenbuie Oct 25, 2024
16db15d
chore: use `_add_defaults_hdr()` in additional place
gadenbuie Oct 25, 2024
af8532a
refactor: don't use `as_css_unit()` just work with strings
gadenbuie Oct 25, 2024
04b3011
refactor: Call `cls` from inside class method
gadenbuie Oct 25, 2024
d030f8d
chore(reload): On `yml` and `yaml` changes
gadenbuie Oct 25, 2024
90b03f5
refactor: brand_yml handles converting typography.base.size to rem
gadenbuie Oct 25, 2024
5d059e2
refactor: simplify finding spec or pkg
gadenbuie Oct 25, 2024
f2d35b6
chore(examples): Add requirements.txt
gadenbuie Oct 25, 2024
83e4a33
docs: Fill out `.from_brand()` documentation
gadenbuie Oct 25, 2024
a34e936
docs: small edit
gadenbuie Oct 25, 2024
9c9b0da
chore: slim type
gadenbuie Oct 25, 2024
439f557
refactor: BrandBootstrapConfig
gadenbuie Oct 25, 2024
805aecd
chore: Add changelog item
gadenbuie Oct 25, 2024
65ae093
chore: use released brand_yml
gadenbuie Oct 25, 2024
bda5054
chore: create new dict
gadenbuie Oct 25, 2024
9b73147
chore: only import brand for type checking
gadenbuie Oct 25, 2024
75dcf2a
Update CHANGELOG.md
cpsievert Oct 25, 2024
51dc33b
Update _theme_brand.py
gadenbuie Oct 25, 2024
339a9aa
example: use layout_sidebar() not page_sidebar()
cpsievert Oct 25, 2024
edbae14
example: pass brand colors along to plotting code (instead of repeati…
cpsievert Oct 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ interlinks:
url: https://matplotlib.org/stable/
python:
url: https://docs.python.org/3/
brand-yml:
url: https://posit-dev.github.io/brand-yml/
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ dependencies = [
]

[project.optional-dependencies]
theme = ["libsass>=0.23.0"]
theme = [
"libsass>=0.23.0",
"brand_yml>=0.1.0rc6"
]
test = [
"pytest>=6.2.4",
"pytest-asyncio>=0.17.2",
Expand Down Expand Up @@ -100,6 +103,7 @@ dev = [
"Flake8-pyproject>=1.2.3",
"isort>=5.10.1",
"libsass>=0.23.0",
"brand_yml>=0.1.0rc5",
"pyright>=1.1.383",
"pre-commit>=2.15.0",
"wheel",
Expand Down
1 change: 1 addition & 0 deletions shiny/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def main() -> None:
"*.htm",
"*.html",
"*.png",
"_brand*.yml",
gadenbuie marked this conversation as resolved.
Show resolved Hide resolved
)
RELOAD_EXCLUDES_DEFAULT = (".*", "*.py[cod]", "__pycache__", "env", "venv")

Expand Down
2 changes: 1 addition & 1 deletion shiny/ui/_html_deps_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def shiny_page_theme_deps(theme: str | Path | Theme | ThemeProvider | None) -> T
if theme is None:
deps_theme = None
elif isinstance(theme, Theme):
deps_theme = theme._html_dependency()
deps_theme = theme._html_dependencies()
elif isinstance(theme, str) and theme.startswith(("http", "//")):
deps_theme = head_content(link(rel="stylesheet", href=theme, type="text/css"))
elif isinstance(theme, (str, Path)):
Expand Down
49 changes: 42 additions & 7 deletions shiny/ui/_theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import textwrap
from typing import Any, Literal, Optional, Sequence, TypeVar

from brand_yml import Brand
gadenbuie marked this conversation as resolved.
Show resolved Hide resolved
from htmltools import HTMLDependency

from .._docstring import add_example
Expand Down Expand Up @@ -460,25 +461,29 @@ def _dep_create(self, css_path: str | pathlib.Path) -> HTMLDependency:
"href": css_path.name,
"data-shiny-theme": self.name or self._preset, # type: ignore
},
# Branded themes re-use this tempdir
all_files=False,
)

def _html_dependency_precompiled(self) -> HTMLDependency:
return self._dep_create(css_path=self._dep_css_precompiled_path())

def _html_dependency(self) -> HTMLDependency:
def _html_dependencies(self) -> list[HTMLDependency]:
"""
Create an `HTMLDependency` object from the theme.

Returns
-------
:
An :class:`~htmltools.HTMLDependency` object representing the theme. In
most cases, you should not need to call this method directly. Instead, pass
the `Theme` object directly to the `theme` argument of any Shiny page
function.
An list of :class:`~htmltools.HTMLDependency` objects representing the
theme. In most cases, you should not need to call this method directly.
Instead, pass the `Theme` object directly to the `theme` argument of any
Shiny page function.
"""
# Note: return a list so that this method can be overridden in subclasses of
# Theme that want to attach additional dependencies to the theme dependency.
if self._can_use_precompiled():
return self._html_dependency_precompiled()
return [self._html_dependency_precompiled()]

css_name = self._dep_css_name()
css_path = os.path.join(self._get_css_tempdir(), css_name)
Expand All @@ -487,7 +492,7 @@ def _html_dependency(self) -> HTMLDependency:
with open(css_path, "w") as css_file:
css_file.write(self.to_css())

return self._dep_create(css_path)
return [self._dep_create(css_path)]

def tagify(self) -> None:
raise SyntaxError(
Expand All @@ -497,6 +502,36 @@ def tagify(self) -> None:
"or any `shiny.ui.page_*()` function (Shiny Core)."
)

@classmethod
def from_brand(cls, brand: str | pathlib.Path | Brand):
gadenbuie marked this conversation as resolved.
Show resolved Hide resolved
"""
Create a custom Shiny theme from a `_brand.yml`

Creates a custom Shiny theme for your brand using
[brand.yml](https://posit-dev.github.io/brand-yml), which may be either an
instance of :class:`brand_yml.Brand` or a :class:`Path` used by
:meth:`brand_yml.Brand.from_yaml` to locate the `_brand.yml` file.

Parameters
----------
brand
A :class:`brand_yml.Brand` instance, or a path to help locate `_brand.yml`.
For a path, you can pass `__file__` or a directory containing the
`_brand.yml` or a path directly to the `_brand.yml` file.

Returns
-------
:
A :class:`shiny.ui.Theme` instance with a custom Shiny theme created from
the brand guidelines (see :class:`brand_yml.Brand`).
"""
from ._theme_brand import ThemeBrand # avoid circular import

if not isinstance(brand, Brand):
brand = Brand.from_yaml(brand)

return ThemeBrand(brand)


def path_pkg_preset(preset: ShinyThemePreset, *args: str) -> str:
"""
Expand Down
Loading
Loading