Coverage for src / turbo_themes / css_variables.py: 89%
92 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-17 07:32 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-17 07:32 +0000
1"""CSS variable generation utilities.
3Provides focused helper functions for generating CSS custom properties
4from theme tokens. Used by ThemeManager.apply_theme_to_css_variables().
5"""
7from __future__ import annotations
9from typing import Dict, Optional
11from .models import Tokens
12from .mapping_config import (
13 get_mapping_config,
14 resolve_token_path,
15 MappingConfig,
16 OptionalGroupConfig,
17)
20def apply_core_mappings(
21 tokens: Tokens,
22 config: Optional[MappingConfig] = None,
23) -> Dict[str, str]:
24 """Apply core token mappings to generate CSS variables.
26 Args:
27 tokens: The theme tokens to map.
28 config: Optional mapping configuration. Uses default if not provided.
30 Returns:
31 Dictionary of CSS variable names to values.
32 """
33 if config is None:
34 config = get_mapping_config()
36 variables: Dict[str, str] = {}
37 prefix = config.prefix
39 for mapping in config.core_mappings:
40 try:
41 value = resolve_token_path(tokens, mapping.token_path)
42 # Try fallback if primary path didn't resolve
43 if value is None and mapping.fallback:
44 value = resolve_token_path(tokens, mapping.fallback)
45 if value is not None: 45 ↛ 39line 45 didn't jump to line 39 because the condition on line 45 was always true
46 variables[f"--{prefix}-{mapping.css_var}"] = str(value)
47 except (AttributeError, KeyError):
48 pass
50 return variables
53def apply_optional_spacing(
54 tokens: Tokens,
55 config: Optional[OptionalGroupConfig] = None,
56 prefix: str = "turbo",
57) -> Dict[str, str]:
58 """Apply optional spacing tokens to generate CSS variables.
60 Args:
61 tokens: The theme tokens containing spacing.
62 config: Optional spacing group configuration.
63 prefix: CSS variable prefix.
65 Returns:
66 Dictionary of spacing CSS variable names to values.
67 """
68 variables: Dict[str, str] = {}
70 if not tokens.spacing:
71 return variables
73 if config is None:
74 mapping_config = get_mapping_config()
75 config = mapping_config.optional_groups.get("spacing")
76 prefix = mapping_config.prefix
78 if config is None: 78 ↛ 79line 78 didn't jump to line 79 because the condition on line 78 was never true
79 return variables
81 spacing = tokens.spacing
82 for prop in config.properties:
83 value = getattr(spacing, prop, None)
84 if value is not None: 84 ↛ 82line 84 didn't jump to line 82 because the condition on line 84 was always true
85 variables[f"--{prefix}-{config.prefix}-{prop}"] = str(value)
87 return variables
90def apply_optional_elevation(
91 tokens: Tokens,
92 config: Optional[OptionalGroupConfig] = None,
93 prefix: str = "turbo",
94) -> Dict[str, str]:
95 """Apply optional elevation tokens to generate CSS variables.
97 Args:
98 tokens: The theme tokens containing elevation.
99 config: Optional elevation group configuration.
100 prefix: CSS variable prefix.
102 Returns:
103 Dictionary of elevation CSS variable names to values.
104 """
105 variables: Dict[str, str] = {}
107 if not tokens.elevation:
108 return variables
110 if config is None:
111 mapping_config = get_mapping_config()
112 config = mapping_config.optional_groups.get("elevation")
113 prefix = mapping_config.prefix
115 if config is None: 115 ↛ 116line 115 didn't jump to line 116 because the condition on line 115 was never true
116 return variables
118 elevation = tokens.elevation
119 for prop in config.properties:
120 value = getattr(elevation, prop, None)
121 if value is not None: 121 ↛ 119line 121 didn't jump to line 119 because the condition on line 121 was always true
122 variables[f"--{prefix}-{config.prefix}-{prop}"] = str(value)
124 return variables
127def apply_optional_animation(
128 tokens: Tokens,
129 config: Optional[OptionalGroupConfig] = None,
130 prefix: str = "turbo",
131) -> Dict[str, str]:
132 """Apply optional animation tokens to generate CSS variables.
134 Args:
135 tokens: The theme tokens containing animation.
136 config: Optional animation group configuration.
137 prefix: CSS variable prefix.
139 Returns:
140 Dictionary of animation CSS variable names to values.
141 """
142 variables: Dict[str, str] = {}
144 if not tokens.animation:
145 return variables
147 if config is None:
148 mapping_config = get_mapping_config()
149 config = mapping_config.optional_groups.get("animation")
150 prefix = mapping_config.prefix
152 if config is None or not config.mappings: 152 ↛ 153line 152 didn't jump to line 153 because the condition on line 152 was never true
153 return variables
155 for mapping in config.mappings:
156 value = resolve_token_path(tokens, mapping.token_path)
157 if value is not None: 157 ↛ 155line 157 didn't jump to line 155 because the condition on line 157 was always true
158 variables[f"--{prefix}-{config.prefix}-{mapping.css_var}"] = str(value)
160 return variables
163def apply_optional_opacity(
164 tokens: Tokens,
165 config: Optional[OptionalGroupConfig] = None,
166 prefix: str = "turbo",
167) -> Dict[str, str]:
168 """Apply optional opacity tokens to generate CSS variables.
170 Args:
171 tokens: The theme tokens containing opacity.
172 config: Optional opacity group configuration.
173 prefix: CSS variable prefix.
175 Returns:
176 Dictionary of opacity CSS variable names to values.
177 """
178 variables: Dict[str, str] = {}
180 if not tokens.opacity:
181 return variables
183 if config is None:
184 mapping_config = get_mapping_config()
185 config = mapping_config.optional_groups.get("opacity")
186 prefix = mapping_config.prefix
188 if config is None: 188 ↛ 189line 188 didn't jump to line 189 because the condition on line 188 was never true
189 return variables
191 opacity = tokens.opacity
192 for prop in config.properties:
193 value = getattr(opacity, prop, None)
194 if value is not None: 194 ↛ 192line 194 didn't jump to line 192 because the condition on line 194 was always true
195 variables[f"--{prefix}-{config.prefix}-{prop}"] = str(value)
197 return variables
200def generate_css_variables(tokens: Tokens) -> Dict[str, str]:
201 """Generate all CSS variables from theme tokens.
203 This is a convenience function that combines all mapping categories:
204 core mappings, spacing, elevation, animation, and opacity.
206 Args:
207 tokens: The theme tokens to convert.
209 Returns:
210 Complete dictionary of CSS variable names to values.
211 """
212 config = get_mapping_config()
213 prefix = config.prefix
215 variables: Dict[str, str] = {}
217 # Apply all mapping categories
218 variables.update(apply_core_mappings(tokens, config))
219 variables.update(
220 apply_optional_spacing(
221 tokens,
222 config.optional_groups.get("spacing"),
223 prefix,
224 )
225 )
226 variables.update(
227 apply_optional_elevation(
228 tokens,
229 config.optional_groups.get("elevation"),
230 prefix,
231 )
232 )
233 variables.update(
234 apply_optional_animation(
235 tokens,
236 config.optional_groups.get("animation"),
237 prefix,
238 )
239 )
240 variables.update(
241 apply_optional_opacity(
242 tokens,
243 config.optional_groups.get("opacity"),
244 prefix,
245 )
246 )
248 return variables