webwidgets 1.0.0__py3-none-any.whl → 1.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
webwidgets/__init__.py CHANGED
@@ -10,9 +10,11 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
- __version__ = "1.0.0" # Dynamically set by build backend
13
+ __version__ = "1.1.1" # Dynamically set by build backend
14
14
 
15
15
  from . import compilation
16
16
  from . import utility
17
+ from .utility.enums import *
18
+ from .utility.sizes.sizes import *
17
19
  from .website import *
18
20
  from .widgets import *
@@ -10,5 +10,6 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
- from .css import compile_css, CSSRule, CompiledCSS, apply_css, \
14
- default_rule_namer
13
+ from .css import apply_css, compile_css, CompiledCSS, default_class_namer
14
+ from .css_rule import ClassRule, CSSRule
15
+ from . import sections
@@ -10,88 +10,102 @@
10
10
  #
11
11
  # =======================================================================
12
12
 
13
+ from .css_rule import ClassRule
13
14
  import itertools
15
+ from .sections.preamble import Preamble
16
+ from .sections.rule_section import RuleSection
14
17
  from typing import Callable, Dict, List, Union
15
18
  from webwidgets.compilation.html.html_node import HTMLNode
16
- from webwidgets.utility.indentation import get_indentation
17
19
  from webwidgets.utility.representation import ReprMixin
18
- from webwidgets.utility.validation import validate_css_identifier
19
-
20
-
21
- class CSSRule(ReprMixin):
22
- """A rule in a style sheet.
23
- """
24
-
25
- def __init__(self, name: str, declarations: Dict[str, str]):
26
- """Stores the name and declarations of the rule.
27
-
28
- :param name: The name of the rule.
29
- :type name: str
30
- :param declarations: The CSS declarations for the rule, specified as a
31
- dictionary where keys are property names and values are their
32
- corresponding values. For example: `{'color': 'red'}`
33
- :type declarations: Dict[str, str]
34
- """
35
- super().__init__()
36
- self.name = name
37
- self.declarations = declarations
38
20
 
39
21
 
40
22
  class CompiledCSS(ReprMixin):
41
23
  """A utility class to hold compiled CSS rules.
42
24
  """
43
25
 
44
- def __init__(self, trees: List[HTMLNode], rules: List[CSSRule],
45
- mapping: Dict[int, List[CSSRule]]):
46
- """Stores compiled CSS rules.
26
+ def __init__(self, trees: List[HTMLNode], core: RuleSection,
27
+ mapping: Dict[int, List[ClassRule]]):
28
+ """Stores compiled CSS rules and their mapping to the nodes in the
29
+ given trees.
47
30
 
48
31
  :param trees: The HTML trees at the origin of the compilation. These
49
32
  are the elements that have been styled with CSS properties.
50
33
  :type trees: List[HTMLNode]
51
- :param rules: The compiled CSS rules.
52
- :type rules: List[CSSRule]
34
+ :param rules: The CSS section containing the compiled CSS rules.
35
+ :type rules: RuleSection
53
36
  :param mapping: A dictionary mapping each node ID to a list of rules
54
37
  that achieve the same style.
55
- :type mapping: Dict[int, List[CSSRule]]
38
+ :type mapping: Dict[int, List[ClassRule]]
56
39
  """
57
40
  super().__init__()
58
41
  self.trees = trees
59
- self.rules = rules
42
+ self.preamble = Preamble()
43
+ self.core = core
60
44
  self.mapping = mapping
61
45
 
62
46
  def to_css(self, indent_size: int = 4) -> str:
63
- """Converts the `rules` dictionary of the :py:class:`CompiledCSS`
64
- object into CSS code.
47
+ """Converts the `preamble` and `core` sections of the
48
+ :py:class:`CompiledCSS` object into CSS code.
65
49
 
66
- Rule names are converted to class selectors. Note that each rule and
67
- property name is validated with :py:func:`validate_css_identifier`
68
- before being converted.
50
+ Sections are converted with their :py:meth:`RuleSection.to_css`
51
+ methods.
69
52
 
70
- :param indent_size: The number of spaces to use for indentation in the
71
- CSS code. Defaults to 4.
53
+ :param indent_size: See :py:meth:`RuleSection.to_css`.
72
54
  :type indent_size: int
73
55
  :return: The CSS code as a string.
74
56
  :rtype: str
75
57
  """
76
- # Initializing code and defining indentation
77
- css_code = ""
78
- indentation = get_indentation(level=1, size=indent_size)
58
+ return '\n\n'.join(
59
+ section.to_css(indent_size=indent_size) for section in (
60
+ self.preamble, self.core
61
+ ))
79
62
 
80
- # Writing down each rule
81
- for i, rule in enumerate(self.rules):
82
- validate_css_identifier(rule.name)
83
- css_code += f".{rule.name}" + " {\n"
84
- for property_name, value in rule.declarations.items():
85
- validate_css_identifier(property_name)
86
- css_code += f"{indentation}{property_name}: {value};\n"
87
- css_code += "}" + ('\n\n' if i < len(self.rules) - 1 else '')
88
63
 
89
- return css_code
64
+ def apply_css(css: CompiledCSS, tree: HTMLNode) -> None:
65
+ """Applies the CSS rules to the given tree.
66
+
67
+ Rules are added as HTML classes to each node with a style in the tree. If a
68
+ node does not have a `class` attribute yet, it will be created for that
69
+ node. Nodes that do not have any style are left untouched.
70
+
71
+ Note that this function is recursive and calls itself on each child node of
72
+ the tree.
73
+
74
+ :param css: The compiled CSS object containing the rules to apply and the
75
+ mapping to each node. It should have been created by invoking
76
+ :py:func:`compile_css` on the given tree, but it can be modified before
77
+ passing it to this function, as long as its content remains consistent.
78
+ :type css: CompiledCSS
79
+ :param tree: The tree to which the CSS rules should be applied. It will be
80
+ modified in place by this function. If you want to keep the original
81
+ tree unchanged, make a deep copy of it using its
82
+ :py:meth:`HTMLNode.copy` method and pass this copy instead.
83
+ :type tree: HTMLNode
84
+ """
85
+ # Only modifying nodes if they have a style (and therefore if the list of
86
+ # rules mapped to them in `css.mapping` is not empty)
87
+ if tree.style:
88
+
89
+ # Listing rules to add as classes. We do not add rules that are already
90
+ # there.
91
+ rules_to_add = [r.name for r in css.mapping[id(tree)] if r.name not in
92
+ tree.attributes.get('class', '').split(' ')]
93
+
94
+ # Updating the class attribute. If it already exists and is not empty,
95
+ # we need to insert a space before adding the CSS classes.
96
+ maybe_space = ' ' if tree.attributes.get(
97
+ 'class', None) and rules_to_add else ''
98
+ tree.attributes['class'] = tree.attributes.get(
99
+ 'class', '') + maybe_space + ' '.join(rules_to_add)
100
+
101
+ # Recursively applying the CSS rules to all child nodes of the tree
102
+ for child in tree.children:
103
+ apply_css(css, child)
90
104
 
91
105
 
92
106
  def compile_css(trees: Union[HTMLNode, List[HTMLNode]],
93
- rule_namer: Callable[[List[CSSRule], int],
94
- str] = None) -> CompiledCSS:
107
+ class_namer: Callable[[List[ClassRule], int],
108
+ str] = None) -> CompiledCSS:
95
109
  """Computes optimized CSS rules from the given HTML trees.
96
110
 
97
111
  The main purpose of this function is to reduce the number of CSS rules
@@ -118,113 +132,82 @@ def compile_css(trees: Union[HTMLNode, List[HTMLNode]],
118
132
  .. code-block:: python
119
133
 
120
134
  >>> compiled_css = compile_css(tree)
121
- >>> print(compiled_css.rules)
135
+ >>> print(compiled_css.core.rules)
122
136
  [
123
- CSSRule(name='r0', declarations={'color': 'blue'}),
124
- CSSRule(name='r1', declarations={'margin': '0'}),
125
- CSSRule(name='r2', declarations={'padding': '0'})
137
+ ClassRule(selector='.c0', declarations={'color': 'blue'}, ...),
138
+ ClassRule(selector='.c1', declarations={'margin': '0'}, ...),
139
+ ClassRule(selector='.c2', declarations={'padding': '0'}, ...)
126
140
  ]
127
141
 
142
+ Internally, each optimized rule gets compiled into a :py:class:`ClassRule`
143
+ object, which represents a CSS rule whose selector targets the HTML `class`
144
+ attribute. Each rule gets assigned a unique HTML class and all classes can
145
+ then be added to the trees with :py:func:`apply_css`. Classes are named
146
+ `"c0"`, `"c1"`, and so on by default, but this naming process can be
147
+ customized using the `class_namer` argument.
148
+
128
149
  :param trees: A single tree or a list of trees to optimize over. All
129
150
  children are recursively included in the compilation.
130
151
  :type trees: Union[HTMLNode, List[HTMLNode]]
131
- :param rule_namer: A callable that takes two arguments, which are the list
152
+ :param class_namer: A callable that takes two arguments, which are the list
132
153
  of all compiled rules and an index within that list, and returns a
133
- unique name for the rule at the given index.
134
-
135
- This argument allows to customize the rule naming process and use names
136
- other than the default `"r0"`, `"r1"`, etc. For example, it can be used
137
- to achieve something similar to Tailwind CSS and name rules according
138
- to what they achieve, e.g. by prefixing their name with `"m"` for
139
- margin rules or `"p"` for padding rules. Note that all rule names will
140
- be validated with the :py:func:`validate_css_identifier` function
141
- before being written into CSS code.
142
-
143
- Defaults to the :py:func:`default_rule_namer` function which implements
144
- a default naming strategy where each rule is named `"r{i}"` where `i`
145
- is the index of the rule in the list.
146
- :type rule_namer: Callable[[List[CSSRule], int], str]
154
+ unique name for the HTML class to associate with the rule at the given
155
+ index.
156
+
157
+ This argument allows to customize the class naming process and use names
158
+ other than the default `"c0"`, `"c1"`, etc. For example, it can be used
159
+ to achieve something similar to Tailwind CSS and name HTML classes
160
+ according to what they achieve, e.g. by prefixing their name with `"m"`
161
+ for margin rules or `"p"` for padding rules. Note that all class
162
+ selectors will be validated with the :py:func:`validate_css_selector`
163
+ function before being written into CSS code.
164
+
165
+ Defaults to the :py:func:`default_class_namer` function which
166
+ implements a default naming strategy where each class is named `"c{i}"`
167
+ where `i` is the index of the rule in the list.
168
+ :type class_namer: Callable[[List[ClassRule], int], str]
147
169
  :return: The :py:class:`CompiledCSS` object containing the optimized rules.
148
170
  Every HTML node present in one or more of the input trees is included
149
171
  in the :py:attr:`CompiledCSS.mapping` attribute, even if the node does
150
- not have a style. Rules are alphabetically ordered by name in the
151
- mapping.
172
+ not have a style. Rules are alphabetically ordered by class name in the
173
+ mapping and in the :py:attr:`CompiledCSS.core` rule section.
152
174
  :rtype: CompiledCSS
153
175
  """
154
176
  # Handling case of a single tree
155
177
  if isinstance(trees, HTMLNode):
156
178
  trees = [trees]
157
179
 
158
- # Handling default rule_namer
159
- rule_namer = default_rule_namer if rule_namer is None else rule_namer
180
+ # Handling default class_namer
181
+ class_namer = default_class_namer if class_namer is None else class_namer
160
182
 
161
183
  # We compute a simple mapping where each CSS property defines its own
162
184
  # ruleset
163
185
  styles = {k: v for tree in trees for k, v in tree.get_styles().items()}
164
186
  properties = set(itertools.chain.from_iterable(s.items()
165
187
  for s in styles.values()))
166
- rules = [CSSRule(None, dict([p])) # Initializing with no name
188
+ rules = [ClassRule("", dict([p])) # Initializing with empty name
167
189
  for p in sorted(properties)]
168
190
  for i, rule in enumerate(rules): # Assigning name from callback
169
- rule.name = rule_namer(rules, i)
191
+ rule.name = class_namer(rules, i)
170
192
  rules = sorted(rules, key=lambda r: r.name) # Sorting by name
171
193
  mapping = {node_id: [r for r in rules if
172
194
  set(r.declarations.items()).issubset(style.items())]
173
195
  for node_id, style in styles.items()}
174
- return CompiledCSS(trees, rules, mapping)
175
-
176
-
177
- def apply_css(css: CompiledCSS, tree: HTMLNode) -> None:
178
- """Applies the CSS rules to the given tree.
179
-
180
- Rules are added as HTML classes to each node with a style in the tree. If a
181
- node does not have a `class` attribute yet, it will be created for that
182
- node. Nodes that do not have any style are left untouched.
183
-
184
- Note that this function is recursive and calls itself on each child node of
185
- the tree.
186
-
187
- :param css: The compiled CSS object containing the rules to apply and the
188
- mapping to each node. It should have been created by invoking
189
- :py:func:`compile_css` on the given tree, but it can be modified before
190
- passing it to this function, as long as its content remains consistent.
191
- :type css: CompiledCSS
192
- :param tree: The tree to which the CSS rules should be applied. It will be
193
- modified in place by this function. If you want to keep the original
194
- tree unchanged, make a deep copy of it using its
195
- :py:meth:`HTMLNode.copy` method and pass this copy instead.
196
- :type tree: HTMLNode
197
- """
198
- # Only modifying nodes if they have a style (and therefore if the list of
199
- # rules mapped to them in `css.mapping` is not empty)
200
- if tree.style:
201
-
202
- # Listing rules to add as classes. We do not add rules that are already
203
- # there.
204
- rules_to_add = [r.name for r in css.mapping[id(tree)] if r.name not in
205
- tree.attributes.get('class', '').split(' ')]
206
196
 
207
- # Updating the class attribute. If it already exists and is not empty,
208
- # we need to insert a space before adding the CSS classes.
209
- maybe_space = ' ' if tree.attributes.get(
210
- 'class', None) and rules_to_add else ''
211
- tree.attributes['class'] = tree.attributes.get(
212
- 'class', '') + maybe_space + ' '.join(rules_to_add)
213
-
214
- # Recursively applying the CSS rules to all child nodes of the tree
215
- for child in tree.children:
216
- apply_css(css, child)
197
+ # Packaging the results into a CompiledCSS object
198
+ core = RuleSection(rules=rules, title="Core")
199
+ return CompiledCSS(trees, core, mapping)
217
200
 
218
201
 
219
- def default_rule_namer(rules: List[CSSRule], index: int) -> str:
220
- """Default rule naming function. Returns a string like "r{i}" where {i} is
202
+ def default_class_namer(rules: List[ClassRule], index: int) -> str:
203
+ """Default class naming function. Returns a string like "c{i}" where {i} is
221
204
  the index of the rule.
222
205
 
223
- :param rules: List of all compiled CSSRule objects. This argument is not
206
+ :param rules: List of all compiled ClassRule objects. This argument is not
224
207
  used in this function, but it can be used in other naming strategies.
225
- :type rules: List[CSSRule]
226
- :param index: Index of the rule being named.
208
+ :type rules: List[ClassRule]
209
+ :param index: Index of the rule whose class is being named.
227
210
  :type index: int
228
- :return: A string like `"r{i}"` where `i` is the index of the rule.
211
+ :return: A string like `"c{i}"` where `i` is the index of the rule.
229
212
  """
230
- return f'r{index}'
213
+ return f'c{index}'
@@ -0,0 +1,106 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from typing import Dict
14
+ from webwidgets.utility.indentation import get_indentation
15
+ from webwidgets.utility.representation import ReprMixin
16
+ from webwidgets.utility.validation import validate_css_identifier, \
17
+ validate_css_selector, validate_css_value
18
+
19
+
20
+ class CSSRule(ReprMixin):
21
+ """A rule in a style sheet.
22
+ """
23
+
24
+ def __init__(self, selector: str, declarations: Dict[str, str]):
25
+ """Stores the selector and declarations of the rule.
26
+
27
+ :param selector: The selector of the rule.
28
+ :type selector: str
29
+ :param declarations: The CSS declarations for the rule, specified as a
30
+ dictionary where keys are property names and values are their
31
+ corresponding values. For example: `{'color': 'red'}`
32
+ :type declarations: Dict[str, str]
33
+ """
34
+ super().__init__()
35
+ self.selector = selector
36
+ self.declarations = declarations
37
+
38
+ def to_css(self, indent_size: int = 4) -> str:
39
+ """Converts the rule into CSS code.
40
+
41
+ The rule's name is converted to a class selector.
42
+
43
+ Note that the rule's name and all property names are validated before
44
+ being converted. The rule's name is validated with
45
+ :py:func:`validate_css_selector` while the property names are validated
46
+ with :py:func:`validate_css_identifier`.
47
+
48
+ :param indent_size: The number of spaces to use for indentation in the
49
+ CSS code. Defaults to 4.
50
+ :type indent_size: int
51
+ :return: The CSS code as a string.
52
+ :rtype: str
53
+ """
54
+ # Defining indentation
55
+ indentation = get_indentation(level=1, size=indent_size)
56
+
57
+ # Validating the selector
58
+ validate_css_selector(self.selector)
59
+
60
+ # Writing down each property
61
+ css_code = self.selector + " {\n"
62
+ for property_name, value in self.declarations.items():
63
+ validate_css_identifier(property_name)
64
+ validate_css_value(value)
65
+ css_code += f"{indentation}{property_name}: {value};\n"
66
+ css_code += "}"
67
+
68
+ return css_code
69
+
70
+
71
+ class ClassRule(CSSRule):
72
+ """A CSS rule that targets a CSS class.
73
+
74
+ The class dynamically sets its selector based on its class name.
75
+ """
76
+
77
+ def __init__(self, name: str, declarations: Dict[str, str]):
78
+ """Creates a new CSS class rule.
79
+
80
+ :param name: The name of the CSS class.
81
+ :type name: str
82
+ :param declarations: See :py:meth:`CSSRule.__init__`.
83
+ :type declarations: Dict[str, str]
84
+ """
85
+ super().__init__(None, declarations) # Starting without a selector
86
+ self._name = None # Starting without a name
87
+ self.name = name # Setting both the selector and the name here
88
+
89
+ @property
90
+ def name(self) -> str:
91
+ """Returns the name of the CSS class.
92
+
93
+ :return: The name of the CSS class.
94
+ :rtype: str
95
+ """
96
+ return self._name
97
+
98
+ @name.setter
99
+ def name(self, value: str) -> None:
100
+ """Sets the name of the CSS class.
101
+
102
+ :param value: The new name of the CSS class.
103
+ :type value: str
104
+ """
105
+ self._name = value
106
+ self.selector = f".{value}" # Updating the selector
@@ -0,0 +1,14 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from .preamble import Preamble
14
+ from .rule_section import RuleSection
@@ -0,0 +1,106 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from abc import ABC, abstractmethod
14
+ from typing import Any
15
+ from webwidgets.utility.representation import ReprMixin
16
+ from webwidgets.utility.validation import validate_css_comment
17
+
18
+
19
+ class CSSSection(ABC, ReprMixin):
20
+ """Abstract base class representing a section of a CSS file.
21
+
22
+ All subclasses of :py:class:`CSSSection` must implement a
23
+ :py:meth:`compile_content` method that returns a string.
24
+ """
25
+
26
+ @staticmethod
27
+ def prettify_title(title: str, min_length: int) -> str:
28
+ """Returns a prettified version of the given title with decorative
29
+ characters `=` around it.
30
+
31
+ This function will add the minimum number of decorative characters to
32
+ the title while keeping symmetry and remaining over the given minimum
33
+ length. In particular, if the given title is already above the minimum
34
+ length, this function will return it as is.
35
+
36
+ :param title: The title to prettify.
37
+ :type title: str
38
+ :param max_length: The minimum length of the prettified title.
39
+ :type max_length: int
40
+ :return: The prettified title.
41
+ :rtype: str
42
+ """
43
+ # If the title is already above min_length, we don't add decorative
44
+ # characters
45
+ if len(title) >= min_length:
46
+ return title
47
+
48
+ # Otherwise, we add decorative characters around the title
49
+ remaining = min_length - len(title)
50
+ characters = "=" * max(((remaining - 1) // 2), 1)
51
+ return characters + ' ' + title + ' ' + characters
52
+
53
+ def __init__(self, title: str = None):
54
+ """Creates a new section with an optional title.
55
+
56
+ :param title: The title of the section. If provided, the section will
57
+ be preceded by a comment containing the title in the output CSS
58
+ code. If None, no title will be used to separate the section from
59
+ the rest of the code.
60
+ :type title: str
61
+ """
62
+ super().__init__()
63
+ self.title = title
64
+
65
+ @abstractmethod
66
+ def compile_content(self) -> str:
67
+ """Converts the content of the CSSSection object (excluding the title)
68
+ into CSS code.
69
+
70
+ This method must be overridden by subclasses to compile specific CSS
71
+ code.
72
+ """
73
+ pass
74
+
75
+ def to_css(self, *args: Any, **kwargs: Any) -> str:
76
+ """Converts the CSSSection object into CSS code.
77
+
78
+ If the section has a title, it will be prettified with
79
+ :py:meth:`CSSSection.prettify_title` and turned into a comment. That
80
+ comment will be validated with :py:func:`validate_css_comment` and
81
+ inserted before the result of :py:meth:`CSSSection.compile_content` in
82
+ the CSS code.
83
+
84
+ If the section has no title, this function will produce the same result
85
+ as :py:meth:`CSSSection.compile_content`.
86
+
87
+ :param args: Arguments to pass to
88
+ :py:meth:`CSSSection.compile_content`.
89
+ :type args: Any
90
+ :param kwargs: Keyword arguments to pass to
91
+ :py:meth:`CSSSection.compile_content`.
92
+ :type kwargs: Any
93
+ :return: The CSS code for the section.
94
+ :rtype: str
95
+ """
96
+ # If no title, we just return the compiled content
97
+ if self.title is None:
98
+ return self.compile_content(*args, **kwargs)
99
+
100
+ # Otherwise, we turn the title into a comment and validate it
101
+ comment = ' ' + CSSSection.prettify_title(self.title, 40) + ' '
102
+ validate_css_comment(comment)
103
+
104
+ # Adding the comment before the compiled content
105
+ return "/*" + comment + "*/\n\n" + \
106
+ self.compile_content(*args, **kwargs)
@@ -0,0 +1,43 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from .rule_section import RuleSection
14
+ from webwidgets.compilation.css.css_rule import CSSRule
15
+
16
+
17
+ class Preamble(RuleSection):
18
+ """A set of CSS rules that apply globally to all HTML elements.
19
+
20
+ The CSS preamble serves as a global default for multiple properties. For
21
+ example, it defines the document's box model and sets all margin and
22
+ padding values to 0.
23
+ """
24
+
25
+ def __init__(self):
26
+ """Creates a new CSS preamble."""
27
+ super().__init__(
28
+ rules=[
29
+ CSSRule("*, *::before, *::after", {
30
+
31
+ # Defining the box model to border-box
32
+ "box-sizing": "border-box",
33
+
34
+ # Setting all margin and padding values to 0
35
+ "margin": "0",
36
+ "padding": "0",
37
+
38
+ # Sets the overflow policy to hidden
39
+ "overflow": "hidden"
40
+ })
41
+ ],
42
+ title="Preamble"
43
+ )
@@ -0,0 +1,43 @@
1
+ # =======================================================================
2
+ #
3
+ # This file is part of WebWidgets, a Python package for designing web
4
+ # UIs.
5
+ #
6
+ # You should have received a copy of the MIT License along with
7
+ # WebWidgets. If not, see <https://opensource.org/license/mit>.
8
+ #
9
+ # Copyright(C) 2025, mlaasri
10
+ #
11
+ # =======================================================================
12
+
13
+ from .css_section import CSSSection
14
+ from typing import List
15
+ from webwidgets.compilation.css.css_rule import CSSRule
16
+
17
+
18
+ class RuleSection(CSSSection):
19
+ """A section containing a set of CSS rules.
20
+ """
21
+
22
+ def __init__(self, rules: List[CSSRule] = None, title: str = None):
23
+ """Creates a new section with the given rules and title.
24
+
25
+ :param rules: A list of CSSRule objects to include in the section.
26
+ :type rules: List[CSSRule]
27
+ :param title: The title of the section.
28
+ :type title: str
29
+ """
30
+ super().__init__(title=title)
31
+ self.rules = [] if rules is None else rules
32
+
33
+ def compile_content(self, indent_size: int = 4) -> str:
34
+ """Compiles the CSS representation of the rules contained in the
35
+ section.
36
+
37
+ :param indent_size: See :py:meth:`CSSRule.to_css`.
38
+ :type indent_size: int
39
+ :return: The CSS representation of the rules.
40
+ :rtype: str
41
+ """
42
+ return "\n\n".join([
43
+ rule.to_css(indent_size=indent_size) for rule in self.rules])
@@ -12,4 +12,4 @@
12
12
 
13
13
  from .html_node import HTMLNode, no_start_tag, no_end_tag, one_line, RawText, \
14
14
  RootNode
15
- from .html_tags import TextNode
15
+ from .html_tags import *