violit 0.0.5__tar.gz → 0.0.7__tar.gz

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.
Files changed (32) hide show
  1. {violit-0.0.5/src/violit.egg-info → violit-0.0.7}/PKG-INFO +1 -1
  2. {violit-0.0.5 → violit-0.0.7}/pyproject.toml +1 -1
  3. {violit-0.0.5 → violit-0.0.7}/src/violit/component.py +38 -38
  4. violit-0.0.7/src/violit/state.py +310 -0
  5. {violit-0.0.5 → violit-0.0.7}/src/violit/theme.py +165 -0
  6. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/chart_widgets.py +253 -253
  7. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/data_widgets.py +27 -9
  8. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/input_widgets.py +745 -745
  9. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/layout_widgets.py +10 -2
  10. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/status_widgets.py +308 -255
  11. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/text_widgets.py +154 -109
  12. {violit-0.0.5 → violit-0.0.7/src/violit.egg-info}/PKG-INFO +1 -1
  13. violit-0.0.5/src/violit/state.py +0 -109
  14. {violit-0.0.5 → violit-0.0.7}/LICENSE +0 -0
  15. {violit-0.0.5 → violit-0.0.7}/README.md +0 -0
  16. {violit-0.0.5 → violit-0.0.7}/setup.cfg +0 -0
  17. {violit-0.0.5 → violit-0.0.7}/src/violit/__init__.py +0 -0
  18. {violit-0.0.5 → violit-0.0.7}/src/violit/app.py +0 -0
  19. {violit-0.0.5 → violit-0.0.7}/src/violit/broadcast.py +0 -0
  20. {violit-0.0.5 → violit-0.0.7}/src/violit/broadcast_primitives.py +0 -0
  21. {violit-0.0.5 → violit-0.0.7}/src/violit/context.py +0 -0
  22. {violit-0.0.5 → violit-0.0.7}/src/violit/engine.py +0 -0
  23. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/__init__.py +0 -0
  24. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/card_widgets.py +0 -0
  25. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/chat_widgets.py +0 -0
  26. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/form_widgets.py +0 -0
  27. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/list_widgets.py +0 -0
  28. {violit-0.0.5 → violit-0.0.7}/src/violit/widgets/media_widgets.py +0 -0
  29. {violit-0.0.5 → violit-0.0.7}/src/violit.egg-info/SOURCES.txt +0 -0
  30. {violit-0.0.5 → violit-0.0.7}/src/violit.egg-info/dependency_links.txt +0 -0
  31. {violit-0.0.5 → violit-0.0.7}/src/violit.egg-info/requires.txt +0 -0
  32. {violit-0.0.5 → violit-0.0.7}/src/violit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: violit
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Violit: Faster than Light, Beautiful as Violet. The High-Performance Python Web Framework (Streamlit Alternative with Zero Rerun).
5
5
  Author-email: Violit Team <violit.company@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "violit"
7
- version = "0.0.5"
7
+ version = "0.0.7"
8
8
  description = "Violit: Faster than Light, Beautiful as Violet. The High-Performance Python Web Framework (Streamlit Alternative with Zero Rerun)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,38 +1,38 @@
1
- from typing import Any, Optional
2
- import html
3
-
4
- class Component:
5
- def __init__(self, tag: Optional[str], id: str, escape_content: bool = False, **props):
6
- self.tag = tag
7
- self.id = id
8
- self.escape_content = escape_content # XSS protection
9
- self.props = props
10
-
11
- def render(self) -> str:
12
- if self.tag is None:
13
- content = str(self.props.get('content', ''))
14
- # Escape content if enabled
15
- return html.escape(content) if self.escape_content else content
16
-
17
- attrs = []
18
- for k, v in self.props.items():
19
- if k == 'content': continue
20
- clean_k = k.replace('_', '-') if not k.startswith('on') else k
21
- if clean_k.startswith('on'):
22
- attrs.append(f'{clean_k}="{v}"')
23
- else:
24
- if v is True: attrs.append(clean_k)
25
- elif v is False or v is None: continue
26
- else:
27
- # Escape attribute values for XSS protection
28
- escaped_v = html.escape(str(v), quote=True)
29
- attrs.append(f'{clean_k}="{escaped_v}"')
30
-
31
- props_str = " ".join(attrs)
32
- content = self.props.get('content', '')
33
-
34
- # Escape content if enabled
35
- if self.escape_content:
36
- content = html.escape(str(content))
37
-
38
- return f"<{self.tag} id=\"{self.id}\" {props_str}>{content}</{self.tag}>"
1
+ from typing import Any, Optional
2
+ import html
3
+
4
+ class Component:
5
+ def __init__(self, tag: Optional[str], id: str, escape_content: bool = False, **props):
6
+ self.tag = tag
7
+ self.id = id
8
+ self.escape_content = escape_content # XSS protection
9
+ self.props = props
10
+
11
+ def render(self) -> str:
12
+ if self.tag is None:
13
+ content = str(self.props.get('content', ''))
14
+ # Escape content if enabled
15
+ return html.escape(content) if self.escape_content else content
16
+
17
+ attrs = []
18
+ for k, v in self.props.items():
19
+ if k == 'content': continue
20
+ clean_k = k.replace('_', '-') if not k.startswith('on') else k
21
+ if clean_k.startswith('on'):
22
+ attrs.append(f'{clean_k}="{v}"')
23
+ else:
24
+ if v is True: attrs.append(clean_k)
25
+ elif v is False or v is None: continue
26
+ else:
27
+ # Escape attribute values for XSS protection
28
+ escaped_v = html.escape(str(v), quote=True)
29
+ attrs.append(f'{clean_k}="{escaped_v}"')
30
+
31
+ props_str = " ".join(attrs)
32
+ content = self.props.get('content', '')
33
+
34
+ # Escape content if enabled
35
+ if self.escape_content:
36
+ content = html.escape(str(content))
37
+
38
+ return f"<{self.tag} id=\"{self.id}\" {props_str}>{content}</{self.tag}>"
@@ -0,0 +1,310 @@
1
+ from typing import Any, Dict, Set
2
+ from cachetools import TTLCache
3
+ from .context import session_ctx, rendering_ctx, app_instance_ref
4
+ from .theme import Theme
5
+
6
+ class DependencyTracker:
7
+ def __init__(self):
8
+ self.subscribers: Dict[str, Set[str]] = {}
9
+
10
+ def register_dependency(self, state_name: str, component_id: str):
11
+ if state_name not in self.subscribers:
12
+ self.subscribers[state_name] = set()
13
+ self.subscribers[state_name].add(component_id)
14
+
15
+ def get_dirty_components(self, state_name: str) -> Set[str]:
16
+ return self.subscribers.get(state_name, set())
17
+
18
+ GLOBAL_STORE = TTLCache(maxsize=1000, ttl=1800)
19
+
20
+ def get_session_store():
21
+ sid = session_ctx.get()
22
+ if sid not in GLOBAL_STORE:
23
+ initial_theme = 'light'
24
+ if app_instance_ref[0]:
25
+ initial_theme = app_instance_ref[0].theme_manager.preset_name
26
+
27
+ GLOBAL_STORE[sid] = {
28
+ 'states': {},
29
+ 'tracker': DependencyTracker(),
30
+ 'builders': {},
31
+ 'actions': {},
32
+ 'component_count': 0,
33
+ 'fragment_components': {},
34
+ 'order': [],
35
+ 'sidebar_order': [],
36
+ 'theme': Theme(initial_theme)
37
+ }
38
+ return GLOBAL_STORE[sid]
39
+
40
+ class State:
41
+ def __init__(self, name: str, default_value: Any):
42
+ object.__setattr__(self, 'name', name)
43
+ object.__setattr__(self, 'default_value', default_value)
44
+
45
+ @property
46
+ def value(self):
47
+ store = get_session_store()
48
+ current_comp_id = rendering_ctx.get()
49
+ if current_comp_id:
50
+ store['tracker'].register_dependency(self.name, current_comp_id)
51
+ return store['states'].get(self.name, self.default_value)
52
+
53
+ @value.setter
54
+ def value(self, new_value: Any):
55
+ self.set(new_value)
56
+
57
+ def set(self, new_value: Any):
58
+ store = get_session_store()
59
+ store['states'][self.name] = new_value
60
+ if 'dirty_states' not in store: store['dirty_states'] = set()
61
+ store['dirty_states'].add(self.name)
62
+
63
+ def __setattr__(self, attr: str, val: Any):
64
+ if attr == 'value':
65
+ self.set(val)
66
+ else:
67
+ object.__setattr__(self, attr, val)
68
+
69
+ def __str__(self):
70
+ return str(self.value)
71
+
72
+ def __call__(self):
73
+ return self.value
74
+
75
+ def __repr__(self):
76
+ return f"State({self.name}, {self.value})"
77
+
78
+ # Reactive Comparison Operators
79
+ def __eq__(self, other): return ComputedState(lambda: self.value == other)
80
+ def __ne__(self, other): return ComputedState(lambda: self.value != other)
81
+ def __lt__(self, other): return ComputedState(lambda: self.value < other)
82
+ def __le__(self, other): return ComputedState(lambda: self.value <= other)
83
+ def __gt__(self, other): return ComputedState(lambda: self.value > other)
84
+ def __ge__(self, other): return ComputedState(lambda: self.value >= other)
85
+
86
+ # Reactive Arithmetic Operators
87
+ def __add__(self, other):
88
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
89
+ def compute():
90
+ self_val = self.value
91
+ # If either is a string, concatenate as strings
92
+ if isinstance(self_val, str) or isinstance(other_val, str):
93
+ return str(self_val) + str(other_val)
94
+ else:
95
+ return self_val + other_val
96
+ return ComputedState(compute)
97
+
98
+ def __radd__(self, other):
99
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
100
+ def compute():
101
+ self_val = self.value
102
+ # If either is a string, concatenate as strings
103
+ if isinstance(self_val, str) or isinstance(other_val, str):
104
+ return str(other_val) + str(self_val)
105
+ else:
106
+ return other_val + self_val
107
+ return ComputedState(compute)
108
+
109
+ def __sub__(self, other):
110
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
111
+ return ComputedState(lambda: self.value - other_val)
112
+
113
+ def __rsub__(self, other):
114
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
115
+ return ComputedState(lambda: other_val - self.value)
116
+
117
+ def __mul__(self, other):
118
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
119
+ def compute():
120
+ self_val = self.value
121
+ # String repetition: "ab" * 3 or 3 * "ab"
122
+ if isinstance(self_val, str) or isinstance(other_val, str):
123
+ # One must be int for string repetition to work
124
+ if isinstance(self_val, str) and isinstance(other_val, int):
125
+ return self_val * other_val
126
+ elif isinstance(other_val, str) and isinstance(self_val, int):
127
+ return self_val * other_val
128
+ else:
129
+ # Both strings? Convert to numeric or error
130
+ return self_val * other_val
131
+ else:
132
+ return self_val * other_val
133
+ return ComputedState(compute)
134
+
135
+ def __rmul__(self, other):
136
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
137
+ def compute():
138
+ self_val = self.value
139
+ # String repetition: "ab" * 3 or 3 * "ab"
140
+ if isinstance(self_val, str) or isinstance(other_val, str):
141
+ if isinstance(other_val, str) and isinstance(self_val, int):
142
+ return other_val * self_val
143
+ elif isinstance(self_val, str) and isinstance(other_val, int):
144
+ return other_val * self_val
145
+ else:
146
+ return other_val * self_val
147
+ else:
148
+ return other_val * self_val
149
+ return ComputedState(compute)
150
+
151
+ def __truediv__(self, other):
152
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
153
+ return ComputedState(lambda: self.value / other_val)
154
+
155
+ def __rtruediv__(self, other):
156
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
157
+ return ComputedState(lambda: other_val / self.value)
158
+
159
+ def __floordiv__(self, other):
160
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
161
+ return ComputedState(lambda: self.value // other_val)
162
+
163
+ def __rfloordiv__(self, other):
164
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
165
+ return ComputedState(lambda: other_val // self.value)
166
+
167
+ def __mod__(self, other):
168
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
169
+ return ComputedState(lambda: self.value % other_val)
170
+
171
+ def __rmod__(self, other):
172
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
173
+ return ComputedState(lambda: other_val % self.value)
174
+
175
+ def __pow__(self, other):
176
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
177
+ return ComputedState(lambda: self.value ** other_val)
178
+
179
+ def __rpow__(self, other):
180
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
181
+ return ComputedState(lambda: other_val ** self.value)
182
+
183
+ # String formatting support
184
+ def __format__(self, format_spec):
185
+ """Support for f-string formatting: f'{state:03d}'"""
186
+ return format(self.value, format_spec)
187
+
188
+
189
+ class ComputedState:
190
+ """A state derived from other states (e.g. expressions)"""
191
+ def __init__(self, func):
192
+ self.func = func
193
+
194
+ @property
195
+ def value(self):
196
+ return self.func()
197
+
198
+ def __bool__(self):
199
+ return bool(self.value)
200
+
201
+ def __call__(self):
202
+ return self.value
203
+
204
+ # Logical operators for chaining
205
+ def __and__(self, other):
206
+ val_other = other.value if hasattr(other, 'value') else other
207
+ return ComputedState(lambda: self.value and val_other)
208
+
209
+ def __or__(self, other):
210
+ val_other = other.value if hasattr(other, 'value') else other
211
+ return ComputedState(lambda: self.value or val_other)
212
+
213
+ # Reactive Arithmetic Operators (Mirroring State)
214
+ def __add__(self, other):
215
+ other_val = other.value if hasattr(other, 'value') else other
216
+ def compute():
217
+ self_val = self.value
218
+ # If either is a string, concatenate as strings
219
+ if isinstance(self_val, str) or isinstance(other_val, str):
220
+ return str(self_val) + str(other_val)
221
+ else:
222
+ return self_val + other_val
223
+ return ComputedState(compute)
224
+
225
+ def __radd__(self, other):
226
+ other_val = other.value if hasattr(other, 'value') else other
227
+ def compute():
228
+ self_val = self.value
229
+ # If either is a string, concatenate as strings
230
+ if isinstance(self_val, str) or isinstance(other_val, str):
231
+ return str(other_val) + str(self_val)
232
+ else:
233
+ return other_val + self_val
234
+ return ComputedState(compute)
235
+
236
+ def __sub__(self, other):
237
+ other_val = other.value if hasattr(other, 'value') else other
238
+ return ComputedState(lambda: self.value - other_val)
239
+
240
+ def __rsub__(self, other):
241
+ other_val = other.value if hasattr(other, 'value') else other
242
+ return ComputedState(lambda: other_val - self.value)
243
+
244
+ def __mul__(self, other):
245
+ other_val = other.value if hasattr(other, 'value') else other
246
+ def compute():
247
+ self_val = self.value
248
+ # String repetition: "ab" * 3 or 3 * "ab"
249
+ if isinstance(self_val, str) or isinstance(other_val, str):
250
+ if isinstance(self_val, str) and isinstance(other_val, int):
251
+ return self_val * other_val
252
+ elif isinstance(other_val, str) and isinstance(self_val, int):
253
+ return self_val * other_val
254
+ else:
255
+ return self_val * other_val
256
+ else:
257
+ return self_val * other_val
258
+ return ComputedState(compute)
259
+
260
+ def __rmul__(self, other):
261
+ other_val = other.value if hasattr(other, 'value') else other
262
+ def compute():
263
+ self_val = self.value
264
+ # String repetition: "ab" * 3 or 3 * "ab"
265
+ if isinstance(self_val, str) or isinstance(other_val, str):
266
+ if isinstance(other_val, str) and isinstance(self_val, int):
267
+ return other_val * self_val
268
+ elif isinstance(self_val, str) and isinstance(other_val, int):
269
+ return other_val * self_val
270
+ else:
271
+ return other_val * self_val
272
+ else:
273
+ return other_val * self_val
274
+ return ComputedState(compute)
275
+
276
+ def __truediv__(self, other):
277
+ other_val = other.value if hasattr(other, 'value') else other
278
+ return ComputedState(lambda: self.value / other_val)
279
+
280
+ def __rtruediv__(self, other):
281
+ other_val = other.value if hasattr(other, 'value') else other
282
+ return ComputedState(lambda: other_val / self.value)
283
+
284
+ def __floordiv__(self, other):
285
+ other_val = other.value if hasattr(other, 'value') else other
286
+ return ComputedState(lambda: self.value // other_val)
287
+
288
+ def __rfloordiv__(self, other):
289
+ other_val = other.value if hasattr(other, 'value') else other
290
+ return ComputedState(lambda: other_val // self.value)
291
+
292
+ def __mod__(self, other):
293
+ other_val = other.value if hasattr(other, 'value') else other
294
+ return ComputedState(lambda: self.value % other_val)
295
+
296
+ def __rmod__(self, other):
297
+ other_val = other.value if hasattr(other, 'value') else other
298
+ return ComputedState(lambda: other_val % self.value)
299
+
300
+ def __pow__(self, other):
301
+ other_val = other.value if hasattr(other, 'value') else other
302
+ return ComputedState(lambda: self.value ** other_val)
303
+
304
+ def __rpow__(self, other):
305
+ other_val = other.value if hasattr(other, 'value') else other
306
+ return ComputedState(lambda: other_val ** self.value)
307
+
308
+ # String formatting support
309
+ def __format__(self, format_spec):
310
+ return format(self.value, format_spec)
@@ -713,6 +713,171 @@ class Theme:
713
713
  -webkit-text-fill-color: transparent !important;
714
714
  }
715
715
  """
716
+ },
717
+ 'violit_light': {
718
+ 'mode': 'light',
719
+ 'primary': '#7C3AED',
720
+ 'secondary': '#8B5CF6',
721
+ 'success': '#10B981',
722
+ 'warning': '#F59E0B',
723
+ 'danger': '#EF4444',
724
+ 'bg': '#FFFFFF',
725
+ 'bg_card': '#FFFFFF',
726
+ 'border': '#E4E4E7',
727
+ 'text': '#18181B',
728
+ 'text_muted': '#71717A',
729
+ 'radius': '6px',
730
+ 'input_border_radius_small': '4px',
731
+ 'input_border_radius_medium': '6px',
732
+ 'input_border_radius_large': '8px',
733
+ 'extra_css': """
734
+ /*
735
+ THEME: Modern Luxury (Restrained)
736
+ Concept: 'Silk & Glass' - Smooth, matte finishes with subtle light play.
737
+ No loud gradients, no flashing flashing animations.
738
+ */
739
+
740
+ body {
741
+ background-color: #FFFFFF;
742
+ /* Very subtle ambient light, barely visible */
743
+ background-image: radial-gradient(circle at 50% 0%, rgba(124, 58, 237, 0.03), transparent 40%);
744
+ }
745
+
746
+ /* Cards: Clean, bordered, soft shadow */
747
+ .card {
748
+ background: #FFFFFF !important;
749
+ border: 1px solid #E4E4E7 !important;
750
+ box-shadow: 0 1px 3px rgba(0,0,0,0.02) !important;
751
+ border-radius: 8px !important;
752
+ transition: box-shadow 0.2s ease, transform 0.2s ease;
753
+ }
754
+ .card:hover {
755
+ box-shadow: 0 8px 24px rgba(0,0,0,0.04) !important;
756
+ border-color: #D4D4D8 !important;
757
+ }
758
+
759
+ /* --- SOPHISTICATED BUTTONS --- */
760
+ /* Matte finish, rich color, soft colored shadow */
761
+
762
+ sl-button::part(base) {
763
+ background: #7C3AED !important; /* Solid Violet 600 */
764
+ border: 1px solid transparent !important; /* Clean edges */
765
+
766
+ color: white !important;
767
+ font-weight: 500 !important; /* Medium weight is more premium than Bold */
768
+ letter-spacing: -0.01em;
769
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
770
+
771
+ border-radius: 6px !important;
772
+
773
+ /* The "Luxury" Touch: A soft, colored shadow glow */
774
+ box-shadow: 0 4px 12px rgba(124, 58, 237, 0.25) !important;
775
+
776
+ transition: all 0.2s cubic-bezier(0.25, 1, 0.5, 1);
777
+ }
778
+
779
+ /* Hover: Subtle Deepening & Lift */
780
+ sl-button::part(base):hover {
781
+ background: #6D28D9 !important; /* Violet 700 */
782
+ transform: translateY(-1px);
783
+ box-shadow: 0 6px 16px rgba(124, 58, 237, 0.35) !important;
784
+ }
785
+
786
+ /* Active: Precise Press */
787
+ sl-button::part(base):active {
788
+ transform: translateY(0px) scale(0.98) !important;
789
+ background: #5B21B6 !important; /* Violet 800 */
790
+ box-shadow: 0 2px 4px rgba(124, 58, 237, 0.15) !important;
791
+ }
792
+
793
+ /* Remove all pseudo-element animations for cleaner look */
794
+ sl-button::part(base)::before,
795
+ sl-button::part(base)::after {
796
+ display: none !important;
797
+ }
798
+
799
+ /* Typography: High contrast, sharp */
800
+ h1, h2, h3 {
801
+ color: #111827;
802
+ font-weight: 700;
803
+ letter-spacing: -0.02em;
804
+ }
805
+ """
806
+ },
807
+ 'violit_dark': {
808
+ 'mode': 'dark',
809
+ 'primary': '#8B5CF6',
810
+ 'secondary': '#6D28D9',
811
+ 'success': '#34D399',
812
+ 'warning': '#FBBF24',
813
+ 'danger': '#F87171',
814
+ 'bg': '#1e1b4b', # Ultra Deep Indigo
815
+ 'bg_card': '#312e81', # Deep Indigo
816
+ 'border': '#4c1d95', # Violet 900
817
+ 'text': '#ede9fe', # Violet 50
818
+ 'text_muted': '#a78bfa',
819
+ 'radius': '3px',
820
+ 'input_border_radius_small': '2px',
821
+ 'input_border_radius_medium': '3px',
822
+ 'input_border_radius_large': '4px',
823
+ 'extra_css': """
824
+ body {
825
+ background: radial-gradient(circle at 50% -20%, #4c1d95 0%, #1e1b4b 50%, #0f0a20 100%);
826
+ }
827
+
828
+ .card {
829
+ background: rgba(49, 46, 129, 0.4) !important;
830
+ backdrop-filter: blur(12px) !important;
831
+ border: 1px solid rgba(139, 92, 246, 0.3) !important;
832
+ box-shadow:
833
+ 0 0 20px rgba(139, 92, 246, 0.05),
834
+ inset 0 0 0 1px rgba(255, 255, 255, 0.05) !important;
835
+ }
836
+
837
+ /* Neon Facet Buttons */
838
+ sl-button::part(base) {
839
+ background: transparent !important;
840
+ border: 1px solid #8B5CF6 !important;
841
+ color: #8B5CF6 !important;
842
+ border-radius: 2px !important;
843
+ box-shadow: 0 0 5px rgba(139, 92, 246, 0.2) !important;
844
+ transition: all 0.2s ease !important;
845
+ text-transform: uppercase;
846
+ font-weight: 700;
847
+ letter-spacing: 1px;
848
+ }
849
+
850
+ /* Hover: Fill with Light */
851
+ sl-button::part(base):hover {
852
+ background: #8B5CF6 !important;
853
+ color: white !important;
854
+ box-shadow: 0 0 20px #8B5CF6, 0 0 40px rgba(139, 92, 246, 0.4) !important;
855
+ text-shadow: 0 1px 2px rgba(0,0,0,0.2);
856
+ transform: scale(1.02) !important;
857
+ border-color: transparent !important;
858
+ }
859
+
860
+ /* Active */
861
+ sl-button::part(base):active {
862
+ transform: scale(0.95) !important;
863
+ box-shadow: 0 0 10px #8B5CF6 !important;
864
+ }
865
+
866
+ h1, h2, h3 {
867
+ color: #ede9fe;
868
+ text-shadow: 0 0 10px rgba(139, 92, 246, 0.5);
869
+ }
870
+
871
+ /* Inputs */
872
+ sl-input::part(base) {
873
+ background: rgba(0,0,0,0.3) !important;
874
+ border: 1px solid #4c1d95 !important;
875
+ }
876
+ sl-input::part(base):focus-within {
877
+ border-color: #8B5CF6 !important;
878
+ box-shadow: 0 0 10px rgba(139, 92, 246, 0.2) !important;
879
+ }
880
+ """
716
881
  }
717
882
  }
718
883