1use crate::classes::ClassBuilder;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub enum FontFamily {
13 Sans,
15 Serif,
17 Mono,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub enum FontSize {
24 Xs,
26 Sm,
28 Base,
30 Lg,
32 Xl,
34 Xl2,
36 Xl3,
38 Xl4,
40 Xl5,
42 Xl6,
44 Xl7,
46 Xl8,
48 Xl9,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub enum FontWeight {
55 Thin,
57 ExtraLight,
59 Light,
61 Normal,
63 Medium,
65 SemiBold,
67 Bold,
69 ExtraBold,
71 Black,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
77pub enum TextAlign {
78 Left,
80 Center,
82 Right,
84 Justify,
86 Start,
88 End,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
94pub enum LineHeight {
95 None,
97 Three,
99 Four,
101 Five,
103 Six,
105 Seven,
107 Eight,
109 Nine,
111 Ten,
113 Tight,
115 Snug,
117 Normal,
119 Relaxed,
121 Loose,
123 Custom(f32),
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
129pub enum LetterSpacing {
130 Tighter,
132 Tight,
134 Normal,
136 Wide,
138 Wider,
140 Widest,
142 Custom(f32),
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
148pub enum TextDecoration {
149 None,
151 Underline,
153 Overline,
155 LineThrough,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
161pub enum TextDecorationStyle {
162 Solid,
164 Double,
166 Dotted,
168 Dashed,
170 Wavy,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
176pub enum TextDecorationThickness {
177 Auto,
179 FromFont,
181 Zero,
183 One,
185 Two,
187 Four,
189 Eight,
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
195pub enum TextUnderlineOffset {
196 Auto,
198 Zero,
200 One,
202 Two,
204 Four,
206 Eight,
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
212pub enum TextTransform {
213 None,
215 Uppercase,
217 Lowercase,
219 Capitalize,
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
225pub enum TextOverflow {
226 Truncate,
228 Ellipsis,
230 Clip,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
236pub enum WhiteSpace {
237 Normal,
239 Nowrap,
241 Pre,
243 PreLine,
245 PreWrap,
247 BreakSpaces,
249}
250
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
253pub enum WordBreak {
254 Normal,
256 BreakAll,
258 BreakWords,
260 KeepAll,
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
266pub enum OverflowWrap {
267 Normal,
269 BreakWord,
271 Anywhere,
273}
274
275impl std::hash::Hash for LineHeight {
276 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
277 match self {
278 LineHeight::None => 0u8.hash(state),
279 LineHeight::Three => 1u8.hash(state),
280 LineHeight::Four => 2u8.hash(state),
281 LineHeight::Five => 3u8.hash(state),
282 LineHeight::Six => 4u8.hash(state),
283 LineHeight::Seven => 5u8.hash(state),
284 LineHeight::Eight => 6u8.hash(state),
285 LineHeight::Nine => 7u8.hash(state),
286 LineHeight::Ten => 8u8.hash(state),
287 LineHeight::Tight => 9u8.hash(state),
288 LineHeight::Snug => 10u8.hash(state),
289 LineHeight::Normal => 11u8.hash(state),
290 LineHeight::Relaxed => 12u8.hash(state),
291 LineHeight::Loose => 13u8.hash(state),
292 LineHeight::Custom(f) => {
293 14u8.hash(state);
294 ((f * 1000.0) as u32).hash(state);
295 }
296 }
297 }
298}
299
300impl std::hash::Hash for LetterSpacing {
301 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
302 match self {
303 LetterSpacing::Tighter => 0u8.hash(state),
304 LetterSpacing::Tight => 1u8.hash(state),
305 LetterSpacing::Normal => 2u8.hash(state),
306 LetterSpacing::Wide => 3u8.hash(state),
307 LetterSpacing::Wider => 4u8.hash(state),
308 LetterSpacing::Widest => 5u8.hash(state),
309 LetterSpacing::Custom(f) => {
310 6u8.hash(state);
311 ((f * 1000.0) as u32).hash(state);
312 }
313 }
314 }
315}
316
317impl std::cmp::Eq for LineHeight {}
318impl std::cmp::Eq for LetterSpacing {}
319
320impl FontFamily {
321 pub fn to_class_name(&self) -> String {
322 match self {
323 FontFamily::Sans => "sans".to_string(),
324 FontFamily::Serif => "serif".to_string(),
325 FontFamily::Mono => "mono".to_string(),
326 }
327 }
328
329 pub fn to_css_value(&self) -> String {
330 match self {
331 FontFamily::Sans => "ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"".to_string(),
332 FontFamily::Serif => "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif".to_string(),
333 FontFamily::Mono => "ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, \"Liberation Mono\", Menlo, monospace".to_string(),
334 }
335 }
336}
337
338impl FontSize {
339 pub fn to_class_name(&self) -> String {
340 match self {
341 FontSize::Xs => "xs".to_string(),
342 FontSize::Sm => "sm".to_string(),
343 FontSize::Base => "base".to_string(),
344 FontSize::Lg => "lg".to_string(),
345 FontSize::Xl => "xl".to_string(),
346 FontSize::Xl2 => "2xl".to_string(),
347 FontSize::Xl3 => "3xl".to_string(),
348 FontSize::Xl4 => "4xl".to_string(),
349 FontSize::Xl5 => "5xl".to_string(),
350 FontSize::Xl6 => "6xl".to_string(),
351 FontSize::Xl7 => "7xl".to_string(),
352 FontSize::Xl8 => "8xl".to_string(),
353 FontSize::Xl9 => "9xl".to_string(),
354 }
355 }
356
357 pub fn to_css_value(&self) -> String {
358 match self {
359 FontSize::Xs => "0.75rem".to_string(),
360 FontSize::Sm => "0.875rem".to_string(),
361 FontSize::Base => "1rem".to_string(),
362 FontSize::Lg => "1.125rem".to_string(),
363 FontSize::Xl => "1.25rem".to_string(),
364 FontSize::Xl2 => "1.5rem".to_string(),
365 FontSize::Xl3 => "1.875rem".to_string(),
366 FontSize::Xl4 => "2.25rem".to_string(),
367 FontSize::Xl5 => "3rem".to_string(),
368 FontSize::Xl6 => "3.75rem".to_string(),
369 FontSize::Xl7 => "4.5rem".to_string(),
370 FontSize::Xl8 => "6rem".to_string(),
371 FontSize::Xl9 => "8rem".to_string(),
372 }
373 }
374}
375
376impl FontWeight {
377 pub fn to_class_name(&self) -> String {
378 match self {
379 FontWeight::Thin => "thin".to_string(),
380 FontWeight::ExtraLight => "extralight".to_string(),
381 FontWeight::Light => "light".to_string(),
382 FontWeight::Normal => "normal".to_string(),
383 FontWeight::Medium => "medium".to_string(),
384 FontWeight::SemiBold => "semibold".to_string(),
385 FontWeight::Bold => "bold".to_string(),
386 FontWeight::ExtraBold => "extrabold".to_string(),
387 FontWeight::Black => "black".to_string(),
388 }
389 }
390
391 pub fn to_css_value(&self) -> String {
392 match self {
393 FontWeight::Thin => "100".to_string(),
394 FontWeight::ExtraLight => "200".to_string(),
395 FontWeight::Light => "300".to_string(),
396 FontWeight::Normal => "400".to_string(),
397 FontWeight::Medium => "500".to_string(),
398 FontWeight::SemiBold => "600".to_string(),
399 FontWeight::Bold => "700".to_string(),
400 FontWeight::ExtraBold => "800".to_string(),
401 FontWeight::Black => "900".to_string(),
402 }
403 }
404}
405
406impl TextAlign {
407 pub fn to_class_name(&self) -> String {
408 match self {
409 TextAlign::Left => "left".to_string(),
410 TextAlign::Center => "center".to_string(),
411 TextAlign::Right => "right".to_string(),
412 TextAlign::Justify => "justify".to_string(),
413 TextAlign::Start => "start".to_string(),
414 TextAlign::End => "end".to_string(),
415 }
416 }
417
418 pub fn to_css_value(&self) -> String {
419 match self {
420 TextAlign::Left => "left".to_string(),
421 TextAlign::Center => "center".to_string(),
422 TextAlign::Right => "right".to_string(),
423 TextAlign::Justify => "justify".to_string(),
424 TextAlign::Start => "start".to_string(),
425 TextAlign::End => "end".to_string(),
426 }
427 }
428}
429
430impl LineHeight {
431 pub fn to_class_name(&self) -> String {
432 match self {
433 LineHeight::None => "none".to_string(),
434 LineHeight::Three => "3".to_string(),
435 LineHeight::Four => "4".to_string(),
436 LineHeight::Five => "5".to_string(),
437 LineHeight::Six => "6".to_string(),
438 LineHeight::Seven => "7".to_string(),
439 LineHeight::Eight => "8".to_string(),
440 LineHeight::Nine => "9".to_string(),
441 LineHeight::Ten => "10".to_string(),
442 LineHeight::Tight => "tight".to_string(),
443 LineHeight::Snug => "snug".to_string(),
444 LineHeight::Normal => "normal".to_string(),
445 LineHeight::Relaxed => "relaxed".to_string(),
446 LineHeight::Loose => "loose".to_string(),
447 LineHeight::Custom(f) => format!("{}", f),
448 }
449 }
450
451 pub fn to_css_value(&self) -> String {
452 match self {
453 LineHeight::None => "1".to_string(),
454 LineHeight::Three => "0.75rem".to_string(),
455 LineHeight::Four => "1rem".to_string(),
456 LineHeight::Five => "1.25rem".to_string(),
457 LineHeight::Six => "1.5rem".to_string(),
458 LineHeight::Seven => "1.75rem".to_string(),
459 LineHeight::Eight => "2rem".to_string(),
460 LineHeight::Nine => "2.25rem".to_string(),
461 LineHeight::Ten => "2.5rem".to_string(),
462 LineHeight::Tight => "1.25".to_string(),
463 LineHeight::Snug => "1.375".to_string(),
464 LineHeight::Normal => "1.5".to_string(),
465 LineHeight::Relaxed => "1.625".to_string(),
466 LineHeight::Loose => "2".to_string(),
467 LineHeight::Custom(f) => f.to_string(),
468 }
469 }
470
471 pub fn all_values() -> Vec<LineHeight> {
473 vec![
474 LineHeight::None,
475 LineHeight::Three,
476 LineHeight::Four,
477 LineHeight::Five,
478 LineHeight::Six,
479 LineHeight::Seven,
480 LineHeight::Eight,
481 LineHeight::Nine,
482 LineHeight::Ten,
483 LineHeight::Tight,
484 LineHeight::Snug,
485 LineHeight::Normal,
486 LineHeight::Relaxed,
487 LineHeight::Loose,
488 ]
489 }
490}
491
492impl LetterSpacing {
493 pub fn to_class_name(&self) -> String {
494 match self {
495 LetterSpacing::Tighter => "tighter".to_string(),
496 LetterSpacing::Tight => "tight".to_string(),
497 LetterSpacing::Normal => "normal".to_string(),
498 LetterSpacing::Wide => "wide".to_string(),
499 LetterSpacing::Wider => "wider".to_string(),
500 LetterSpacing::Widest => "widest".to_string(),
501 LetterSpacing::Custom(f) => format!("{}", f),
502 }
503 }
504
505 pub fn to_css_value(&self) -> String {
506 match self {
507 LetterSpacing::Tighter => "-0.05em".to_string(),
508 LetterSpacing::Tight => "-0.025em".to_string(),
509 LetterSpacing::Normal => "0em".to_string(),
510 LetterSpacing::Wide => "0.025em".to_string(),
511 LetterSpacing::Wider => "0.05em".to_string(),
512 LetterSpacing::Widest => "0.1em".to_string(),
513 LetterSpacing::Custom(f) => format!("{}em", f),
514 }
515 }
516}
517
518impl TextDecoration {
519 pub fn to_class_name(&self) -> String {
520 match self {
521 TextDecoration::None => "no-underline".to_string(),
522 TextDecoration::Underline => "underline".to_string(),
523 TextDecoration::Overline => "overline".to_string(),
524 TextDecoration::LineThrough => "line-through".to_string(),
525 }
526 }
527
528 pub fn to_css_value(&self) -> String {
529 match self {
530 TextDecoration::None => "none".to_string(),
531 TextDecoration::Underline => "underline".to_string(),
532 TextDecoration::Overline => "overline".to_string(),
533 TextDecoration::LineThrough => "line-through".to_string(),
534 }
535 }
536}
537
538impl TextDecorationStyle {
539 pub fn to_class_name(&self) -> String {
540 match self {
541 TextDecorationStyle::Solid => "decoration-solid".to_string(),
542 TextDecorationStyle::Double => "decoration-double".to_string(),
543 TextDecorationStyle::Dotted => "decoration-dotted".to_string(),
544 TextDecorationStyle::Dashed => "decoration-dashed".to_string(),
545 TextDecorationStyle::Wavy => "decoration-wavy".to_string(),
546 }
547 }
548
549 pub fn to_css_value(&self) -> String {
550 match self {
551 TextDecorationStyle::Solid => "solid".to_string(),
552 TextDecorationStyle::Double => "double".to_string(),
553 TextDecorationStyle::Dotted => "dotted".to_string(),
554 TextDecorationStyle::Dashed => "dashed".to_string(),
555 TextDecorationStyle::Wavy => "wavy".to_string(),
556 }
557 }
558}
559
560impl TextDecorationThickness {
561 pub fn to_class_name(&self) -> String {
562 match self {
563 TextDecorationThickness::Auto => "decoration-auto".to_string(),
564 TextDecorationThickness::FromFont => "decoration-from-font".to_string(),
565 TextDecorationThickness::Zero => "decoration-0".to_string(),
566 TextDecorationThickness::One => "decoration-1".to_string(),
567 TextDecorationThickness::Two => "decoration-2".to_string(),
568 TextDecorationThickness::Four => "decoration-4".to_string(),
569 TextDecorationThickness::Eight => "decoration-8".to_string(),
570 }
571 }
572
573 pub fn to_css_value(&self) -> String {
574 match self {
575 TextDecorationThickness::Auto => "auto".to_string(),
576 TextDecorationThickness::FromFont => "from-font".to_string(),
577 TextDecorationThickness::Zero => "0px".to_string(),
578 TextDecorationThickness::One => "1px".to_string(),
579 TextDecorationThickness::Two => "2px".to_string(),
580 TextDecorationThickness::Four => "4px".to_string(),
581 TextDecorationThickness::Eight => "8px".to_string(),
582 }
583 }
584}
585
586impl TextUnderlineOffset {
587 pub fn to_class_name(&self) -> String {
588 match self {
589 TextUnderlineOffset::Auto => "underline-offset-auto".to_string(),
590 TextUnderlineOffset::Zero => "underline-offset-0".to_string(),
591 TextUnderlineOffset::One => "underline-offset-1".to_string(),
592 TextUnderlineOffset::Two => "underline-offset-2".to_string(),
593 TextUnderlineOffset::Four => "underline-offset-4".to_string(),
594 TextUnderlineOffset::Eight => "underline-offset-8".to_string(),
595 }
596 }
597
598 pub fn to_css_value(&self) -> String {
599 match self {
600 TextUnderlineOffset::Auto => "auto".to_string(),
601 TextUnderlineOffset::Zero => "0px".to_string(),
602 TextUnderlineOffset::One => "1px".to_string(),
603 TextUnderlineOffset::Two => "2px".to_string(),
604 TextUnderlineOffset::Four => "4px".to_string(),
605 TextUnderlineOffset::Eight => "8px".to_string(),
606 }
607 }
608}
609
610impl TextTransform {
611 pub fn to_class_name(&self) -> String {
612 match self {
613 TextTransform::None => "normal-case".to_string(),
614 TextTransform::Uppercase => "uppercase".to_string(),
615 TextTransform::Lowercase => "lowercase".to_string(),
616 TextTransform::Capitalize => "capitalize".to_string(),
617 }
618 }
619
620 pub fn to_css_value(&self) -> String {
621 match self {
622 TextTransform::None => "none".to_string(),
623 TextTransform::Uppercase => "uppercase".to_string(),
624 TextTransform::Lowercase => "lowercase".to_string(),
625 TextTransform::Capitalize => "capitalize".to_string(),
626 }
627 }
628}
629
630impl TextOverflow {
631 pub fn to_class_name(&self) -> String {
632 match self {
633 TextOverflow::Truncate => "truncate".to_string(),
634 TextOverflow::Ellipsis => "text-ellipsis".to_string(),
635 TextOverflow::Clip => "text-clip".to_string(),
636 }
637 }
638
639 pub fn to_css_value(&self) -> String {
640 match self {
641 TextOverflow::Truncate => "truncate".to_string(),
642 TextOverflow::Ellipsis => "ellipsis".to_string(),
643 TextOverflow::Clip => "clip".to_string(),
644 }
645 }
646}
647
648impl WhiteSpace {
649 pub fn to_class_name(&self) -> String {
650 match self {
651 WhiteSpace::Normal => "whitespace-normal".to_string(),
652 WhiteSpace::Nowrap => "whitespace-nowrap".to_string(),
653 WhiteSpace::Pre => "whitespace-pre".to_string(),
654 WhiteSpace::PreLine => "whitespace-pre-line".to_string(),
655 WhiteSpace::PreWrap => "whitespace-pre-wrap".to_string(),
656 WhiteSpace::BreakSpaces => "whitespace-break-spaces".to_string(),
657 }
658 }
659
660 pub fn to_css_value(&self) -> String {
661 match self {
662 WhiteSpace::Normal => "normal".to_string(),
663 WhiteSpace::Nowrap => "nowrap".to_string(),
664 WhiteSpace::Pre => "pre".to_string(),
665 WhiteSpace::PreLine => "pre-line".to_string(),
666 WhiteSpace::PreWrap => "pre-wrap".to_string(),
667 WhiteSpace::BreakSpaces => "break-spaces".to_string(),
668 }
669 }
670}
671
672impl WordBreak {
673 pub fn to_class_name(&self) -> String {
674 match self {
675 WordBreak::Normal => "break-normal".to_string(),
676 WordBreak::BreakAll => "break-all".to_string(),
677 WordBreak::BreakWords => "break-words".to_string(),
678 WordBreak::KeepAll => "break-keep".to_string(),
679 }
680 }
681
682 pub fn to_css_value(&self) -> String {
683 match self {
684 WordBreak::Normal => "normal".to_string(),
685 WordBreak::BreakAll => "break-all".to_string(),
686 WordBreak::BreakWords => "break-words".to_string(),
687 WordBreak::KeepAll => "keep-all".to_string(),
688 }
689 }
690}
691
692impl OverflowWrap {
693 pub fn to_class_name(&self) -> String {
694 match self {
695 OverflowWrap::Normal => "overflow-wrap-normal".to_string(),
696 OverflowWrap::BreakWord => "overflow-wrap-break".to_string(),
697 OverflowWrap::Anywhere => "overflow-wrap-anywhere".to_string(),
698 }
699 }
700
701 pub fn to_css_value(&self) -> String {
702 match self {
703 OverflowWrap::Normal => "normal".to_string(),
704 OverflowWrap::BreakWord => "break-word".to_string(),
705 OverflowWrap::Anywhere => "anywhere".to_string(),
706 }
707 }
708}
709
710impl fmt::Display for FontFamily {
711 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712 write!(f, "{}", self.to_class_name())
713 }
714}
715
716impl fmt::Display for FontSize {
717 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
718 write!(f, "{}", self.to_class_name())
719 }
720}
721
722impl fmt::Display for FontWeight {
723 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724 write!(f, "{}", self.to_class_name())
725 }
726}
727
728impl fmt::Display for TextAlign {
729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730 write!(f, "{}", self.to_class_name())
731 }
732}
733
734impl fmt::Display for LineHeight {
735 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
736 write!(f, "{}", self.to_class_name())
737 }
738}
739
740impl fmt::Display for LetterSpacing {
741 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742 write!(f, "{}", self.to_class_name())
743 }
744}
745
746impl fmt::Display for TextDecoration {
747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748 write!(f, "{}", self.to_class_name())
749 }
750}
751
752impl fmt::Display for TextDecorationStyle {
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 write!(f, "{}", self.to_class_name())
755 }
756}
757
758impl fmt::Display for TextDecorationThickness {
759 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
760 write!(f, "{}", self.to_class_name())
761 }
762}
763
764impl fmt::Display for TextUnderlineOffset {
765 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
766 write!(f, "{}", self.to_class_name())
767 }
768}
769
770impl fmt::Display for TextTransform {
771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772 write!(f, "{}", self.to_class_name())
773 }
774}
775
776impl fmt::Display for OverflowWrap {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 write!(f, "{}", self.to_class_name())
779 }
780}
781
782pub trait FontFamilyUtilities {
784 fn font_family(self, family: FontFamily) -> Self;
786}
787
788impl FontFamilyUtilities for ClassBuilder {
789 fn font_family(self, family: FontFamily) -> Self {
790 self.class(format!("font-{}", family.to_class_name()))
791 }
792}
793
794pub trait FontSizeUtilities {
796 fn font_size(self, size: FontSize) -> Self;
798}
799
800impl FontSizeUtilities for ClassBuilder {
801 fn font_size(self, size: FontSize) -> Self {
802 self.class(format!("text-{}", size.to_class_name()))
803 }
804}
805
806pub trait FontWeightUtilities {
808 fn font_weight(self, weight: FontWeight) -> Self;
810}
811
812impl FontWeightUtilities for ClassBuilder {
813 fn font_weight(self, weight: FontWeight) -> Self {
814 self.class(format!("font-{}", weight.to_class_name()))
815 }
816}
817
818pub trait TextAlignUtilities {
820 fn text_align(self, align: TextAlign) -> Self;
822}
823
824impl TextAlignUtilities for ClassBuilder {
825 fn text_align(self, align: TextAlign) -> Self {
826 self.class(format!("text-{}", align.to_class_name()))
827 }
828}
829
830pub trait LineHeightUtilities {
832 fn line_height(self, height: LineHeight) -> Self;
834}
835
836impl LineHeightUtilities for ClassBuilder {
837 fn line_height(self, height: LineHeight) -> Self {
838 self.class(format!("leading-{}", height.to_class_name()))
839 }
840}
841
842pub trait LetterSpacingUtilities {
844 fn letter_spacing(self, spacing: LetterSpacing) -> Self;
846}
847
848impl LetterSpacingUtilities for ClassBuilder {
849 fn letter_spacing(self, spacing: LetterSpacing) -> Self {
850 self.class(format!("tracking-{}", spacing.to_class_name()))
851 }
852}
853
854pub trait TextDecorationUtilities {
856 fn text_decoration(self, decoration: TextDecoration) -> Self;
858}
859
860impl TextDecorationUtilities for ClassBuilder {
861 fn text_decoration(self, decoration: TextDecoration) -> Self {
862 self.class(decoration.to_class_name())
863 }
864}
865
866pub trait TextTransformUtilities {
868 fn text_transform(self, transform: TextTransform) -> Self;
870}
871
872impl TextTransformUtilities for ClassBuilder {
873 fn text_transform(self, transform: TextTransform) -> Self {
874 self.class(transform.to_class_name())
875 }
876}
877
878pub trait TextOverflowUtilities {
880 fn text_overflow(self, overflow: TextOverflow) -> Self;
882}
883
884impl TextOverflowUtilities for ClassBuilder {
885 fn text_overflow(self, overflow: TextOverflow) -> Self {
886 self.class(overflow.to_class_name())
887 }
888}
889
890pub trait WhiteSpaceUtilities {
892 fn white_space(self, space: WhiteSpace) -> Self;
894}
895
896impl WhiteSpaceUtilities for ClassBuilder {
897 fn white_space(self, space: WhiteSpace) -> Self {
898 self.class(space.to_class_name())
899 }
900}
901
902pub trait WordBreakUtilities {
904 fn word_break(self, break_type: WordBreak) -> Self;
906}
907
908impl WordBreakUtilities for ClassBuilder {
909 fn word_break(self, break_type: WordBreak) -> Self {
910 self.class(break_type.to_class_name())
911 }
912}
913
914pub trait OverflowWrapUtilities {
916 fn overflow_wrap(self, wrap_type: OverflowWrap) -> Self;
918}
919
920impl OverflowWrapUtilities for ClassBuilder {
921 fn overflow_wrap(self, wrap_type: OverflowWrap) -> Self {
922 self.class(wrap_type.to_class_name())
923 }
924}
925
926pub trait TextDecorationStyleUtilities {
928 fn text_decoration_style(self, style: TextDecorationStyle) -> Self;
930}
931
932impl TextDecorationStyleUtilities for ClassBuilder {
933 fn text_decoration_style(self, style: TextDecorationStyle) -> Self {
934 self.class(style.to_class_name())
935 }
936}
937
938pub trait TextDecorationThicknessUtilities {
940 fn text_decoration_thickness(self, thickness: TextDecorationThickness) -> Self;
942}
943
944impl TextDecorationThicknessUtilities for ClassBuilder {
945 fn text_decoration_thickness(self, thickness: TextDecorationThickness) -> Self {
946 self.class(thickness.to_class_name())
947 }
948}
949
950pub trait TextUnderlineOffsetUtilities {
952 fn text_underline_offset(self, offset: TextUnderlineOffset) -> Self;
954}
955
956impl TextUnderlineOffsetUtilities for ClassBuilder {
957 fn text_underline_offset(self, offset: TextUnderlineOffset) -> Self {
958 self.class(offset.to_class_name())
959 }
960}
961
962#[cfg(test)]
963mod tests {
964 use super::*;
965
966 #[test]
967 fn test_font_family_utilities() {
968 let classes = ClassBuilder::new()
969 .font_family(FontFamily::Sans)
970 .font_family(FontFamily::Serif)
971 .font_family(FontFamily::Mono)
972 .build();
973
974 let css_classes = classes.to_css_classes();
975 assert!(css_classes.contains("font-sans"));
976 assert!(css_classes.contains("font-serif"));
977 assert!(css_classes.contains("font-mono"));
978 }
979
980 #[test]
981 fn test_font_size_utilities() {
982 let classes = ClassBuilder::new()
983 .font_size(FontSize::Xs)
984 .font_size(FontSize::Sm)
985 .font_size(FontSize::Base)
986 .font_size(FontSize::Lg)
987 .font_size(FontSize::Xl)
988 .build();
989
990 let css_classes = classes.to_css_classes();
991 assert!(css_classes.contains("text-xs"));
992 assert!(css_classes.contains("text-sm"));
993 assert!(css_classes.contains("text-base"));
994 assert!(css_classes.contains("text-lg"));
995 assert!(css_classes.contains("text-xl"));
996 }
997
998 #[test]
999 fn test_font_weight_utilities() {
1000 let classes = ClassBuilder::new()
1001 .font_weight(FontWeight::Thin)
1002 .font_weight(FontWeight::Normal)
1003 .font_weight(FontWeight::Bold)
1004 .font_weight(FontWeight::Black)
1005 .build();
1006
1007 let css_classes = classes.to_css_classes();
1008 assert!(css_classes.contains("font-thin"));
1009 assert!(css_classes.contains("font-normal"));
1010 assert!(css_classes.contains("font-bold"));
1011 assert!(css_classes.contains("font-black"));
1012 }
1013
1014 #[test]
1015 fn test_text_align_utilities() {
1016 let classes = ClassBuilder::new()
1017 .text_align(TextAlign::Left)
1018 .text_align(TextAlign::Center)
1019 .text_align(TextAlign::Right)
1020 .text_align(TextAlign::Justify)
1021 .build();
1022
1023 let css_classes = classes.to_css_classes();
1024 assert!(css_classes.contains("text-left"));
1025 assert!(css_classes.contains("text-center"));
1026 assert!(css_classes.contains("text-right"));
1027 assert!(css_classes.contains("text-justify"));
1028 }
1029
1030 #[test]
1031 fn test_line_height_utilities() {
1032 let classes = ClassBuilder::new()
1033 .line_height(LineHeight::Tight)
1034 .line_height(LineHeight::Normal)
1035 .line_height(LineHeight::Relaxed)
1036 .line_height(LineHeight::Custom(1.75))
1037 .build();
1038
1039 let css_classes = classes.to_css_classes();
1040 assert!(css_classes.contains("leading-tight"));
1041 assert!(css_classes.contains("leading-normal"));
1042 assert!(css_classes.contains("leading-relaxed"));
1043 assert!(css_classes.contains("leading-1.75"));
1044 }
1045
1046 #[test]
1047 fn test_letter_spacing_utilities() {
1048 let classes = ClassBuilder::new()
1049 .letter_spacing(LetterSpacing::Tight)
1050 .letter_spacing(LetterSpacing::Normal)
1051 .letter_spacing(LetterSpacing::Wide)
1052 .letter_spacing(LetterSpacing::Custom(0.1))
1053 .build();
1054
1055 let css_classes = classes.to_css_classes();
1056 assert!(css_classes.contains("tracking-tight"));
1057 assert!(css_classes.contains("tracking-normal"));
1058 assert!(css_classes.contains("tracking-wide"));
1059 assert!(css_classes.contains("tracking-0.1"));
1060 }
1061
1062 #[test]
1063 fn test_text_decoration_utilities() {
1064 let classes = ClassBuilder::new()
1065 .text_decoration(TextDecoration::None)
1066 .text_decoration(TextDecoration::Underline)
1067 .text_decoration(TextDecoration::LineThrough)
1068 .build();
1069
1070 let css_classes = classes.to_css_classes();
1071 assert!(css_classes.contains("no-underline"));
1072 assert!(css_classes.contains("underline"));
1073 assert!(css_classes.contains("line-through"));
1074 }
1075
1076 #[test]
1077 fn test_text_transform_utilities() {
1078 let classes = ClassBuilder::new()
1079 .text_transform(TextTransform::None)
1080 .text_transform(TextTransform::Uppercase)
1081 .text_transform(TextTransform::Lowercase)
1082 .text_transform(TextTransform::Capitalize)
1083 .build();
1084
1085 let css_classes = classes.to_css_classes();
1086 assert!(css_classes.contains("normal-case"));
1087 assert!(css_classes.contains("uppercase"));
1088 assert!(css_classes.contains("lowercase"));
1089 assert!(css_classes.contains("capitalize"));
1090 }
1091
1092 #[test]
1093 fn test_text_overflow_utilities() {
1094 let classes = ClassBuilder::new()
1095 .text_overflow(TextOverflow::Truncate)
1096 .text_overflow(TextOverflow::Ellipsis)
1097 .text_overflow(TextOverflow::Clip)
1098 .build();
1099
1100 let css_classes = classes.to_css_classes();
1101 assert!(css_classes.contains("truncate"));
1102 assert!(css_classes.contains("text-ellipsis"));
1103 assert!(css_classes.contains("text-clip"));
1104 }
1105
1106 #[test]
1107 fn test_white_space_utilities() {
1108 let classes = ClassBuilder::new()
1109 .white_space(WhiteSpace::Normal)
1110 .white_space(WhiteSpace::Nowrap)
1111 .white_space(WhiteSpace::Pre)
1112 .build();
1113
1114 let css_classes = classes.to_css_classes();
1115 assert!(css_classes.contains("whitespace-normal"));
1116 assert!(css_classes.contains("whitespace-nowrap"));
1117 assert!(css_classes.contains("whitespace-pre"));
1118 }
1119
1120 #[test]
1121 fn test_word_break_utilities() {
1122 let classes = ClassBuilder::new()
1123 .word_break(WordBreak::Normal)
1124 .word_break(WordBreak::BreakAll)
1125 .word_break(WordBreak::BreakWords)
1126 .build();
1127
1128 let css_classes = classes.to_css_classes();
1129 assert!(css_classes.contains("break-normal"));
1130 assert!(css_classes.contains("break-all"));
1131 assert!(css_classes.contains("break-words"));
1132 }
1133
1134 #[test]
1135 fn test_complex_typography_combination() {
1136 let classes = ClassBuilder::new()
1137 .font_family(FontFamily::Sans)
1138 .font_size(FontSize::Lg)
1139 .font_weight(FontWeight::Bold)
1140 .text_align(TextAlign::Center)
1141 .line_height(LineHeight::Relaxed)
1142 .letter_spacing(LetterSpacing::Wide)
1143 .text_decoration(TextDecoration::Underline)
1144 .text_transform(TextTransform::Uppercase)
1145 .build();
1146
1147 let css_classes = classes.to_css_classes();
1148 assert!(css_classes.contains("font-sans"));
1149 assert!(css_classes.contains("text-lg"));
1150 assert!(css_classes.contains("font-bold"));
1151 assert!(css_classes.contains("text-center"));
1152 assert!(css_classes.contains("leading-relaxed"));
1153 assert!(css_classes.contains("tracking-wide"));
1154 assert!(css_classes.contains("underline"));
1155 assert!(css_classes.contains("uppercase"));
1156 }
1157
1158 #[test]
1160 fn test_all_tailwind_font_sizes() {
1161 let test_values = vec![
1163 (FontSize::Xs, "text-xs"),
1164 (FontSize::Sm, "text-sm"),
1165 (FontSize::Base, "text-base"),
1166 (FontSize::Lg, "text-lg"),
1167 (FontSize::Xl, "text-xl"),
1168 (FontSize::Xl2, "text-2xl"),
1169 (FontSize::Xl3, "text-3xl"),
1170 (FontSize::Xl4, "text-4xl"),
1171 (FontSize::Xl5, "text-5xl"),
1172 (FontSize::Xl6, "text-6xl"),
1173 (FontSize::Xl7, "text-7xl"),
1174 (FontSize::Xl8, "text-8xl"),
1175 (FontSize::Xl9, "text-9xl"),
1176 ];
1177
1178 for (value, expected_class) in test_values {
1179 let classes = ClassBuilder::new().font_size(value).build();
1180 let css_classes = classes.to_css_classes();
1181 assert!(css_classes.contains(expected_class),
1182 "Missing font size: {} (expected class: {})",
1183 format!("{:?}", value), expected_class);
1184 }
1185 }
1186
1187 #[test]
1189 fn test_week4_typography_utilities() {
1190 let classes = ClassBuilder::new()
1192 .font_size(FontSize::Xs)
1194 .font_size(FontSize::Xl9)
1195 .font_weight(FontWeight::Thin)
1197 .font_weight(FontWeight::Black)
1198 .line_height(LineHeight::Tight)
1200 .line_height(LineHeight::Relaxed)
1201 .letter_spacing(LetterSpacing::Tighter)
1203 .letter_spacing(LetterSpacing::Widest)
1204 .text_decoration(TextDecoration::Underline)
1206 .text_decoration(TextDecoration::None)
1207 .text_decoration(TextDecoration::LineThrough)
1208 .text_transform(TextTransform::Uppercase)
1210 .text_transform(TextTransform::Lowercase)
1211 .text_transform(TextTransform::Capitalize)
1212 .build();
1213
1214 let css_classes = classes.to_css_classes();
1215
1216 assert!(css_classes.contains("text-xs"));
1218 assert!(css_classes.contains("text-9xl"));
1219
1220 assert!(css_classes.contains("font-thin"));
1222 assert!(css_classes.contains("font-black"));
1223
1224 assert!(css_classes.contains("leading-tight"));
1226 assert!(css_classes.contains("leading-relaxed"));
1227
1228 assert!(css_classes.contains("tracking-tighter"));
1230 assert!(css_classes.contains("tracking-widest"));
1231
1232 assert!(css_classes.contains("underline"));
1234 assert!(css_classes.contains("no-underline"));
1235 assert!(css_classes.contains("line-through"));
1236
1237 assert!(css_classes.contains("uppercase"));
1239 assert!(css_classes.contains("lowercase"));
1240 assert!(css_classes.contains("capitalize"));
1241 }
1242
1243 #[test]
1245 fn test_extended_line_height_utilities() {
1246 let classes = ClassBuilder::new()
1247 .line_height(LineHeight::Three)
1248 .line_height(LineHeight::Four)
1249 .line_height(LineHeight::Five)
1250 .line_height(LineHeight::Six)
1251 .line_height(LineHeight::Seven)
1252 .line_height(LineHeight::Eight)
1253 .line_height(LineHeight::Nine)
1254 .line_height(LineHeight::Ten)
1255 .build();
1256
1257 let css_classes = classes.to_css_classes();
1258 assert!(css_classes.contains("leading-3"));
1259 assert!(css_classes.contains("leading-4"));
1260 assert!(css_classes.contains("leading-5"));
1261 assert!(css_classes.contains("leading-6"));
1262 assert!(css_classes.contains("leading-7"));
1263 assert!(css_classes.contains("leading-8"));
1264 assert!(css_classes.contains("leading-9"));
1265 assert!(css_classes.contains("leading-10"));
1266 }
1267
1268 #[test]
1270 fn test_extended_text_decoration_utilities() {
1271 let classes = ClassBuilder::new()
1272 .text_decoration_style(TextDecorationStyle::Solid)
1273 .text_decoration_style(TextDecorationStyle::Double)
1274 .text_decoration_style(TextDecorationStyle::Dotted)
1275 .text_decoration_style(TextDecorationStyle::Dashed)
1276 .text_decoration_style(TextDecorationStyle::Wavy)
1277 .text_decoration_thickness(TextDecorationThickness::Auto)
1278 .text_decoration_thickness(TextDecorationThickness::FromFont)
1279 .text_decoration_thickness(TextDecorationThickness::One)
1280 .text_decoration_thickness(TextDecorationThickness::Two)
1281 .text_decoration_thickness(TextDecorationThickness::Four)
1282 .text_underline_offset(TextUnderlineOffset::Auto)
1283 .text_underline_offset(TextUnderlineOffset::Zero)
1284 .text_underline_offset(TextUnderlineOffset::One)
1285 .text_underline_offset(TextUnderlineOffset::Two)
1286 .text_underline_offset(TextUnderlineOffset::Four)
1287 .build();
1288
1289 let css_classes = classes.to_css_classes();
1290
1291 assert!(css_classes.contains("decoration-solid"));
1293 assert!(css_classes.contains("decoration-double"));
1294 assert!(css_classes.contains("decoration-dotted"));
1295 assert!(css_classes.contains("decoration-dashed"));
1296 assert!(css_classes.contains("decoration-wavy"));
1297
1298 assert!(css_classes.contains("decoration-auto"));
1300 assert!(css_classes.contains("decoration-from-font"));
1301 assert!(css_classes.contains("decoration-1"));
1302 assert!(css_classes.contains("decoration-2"));
1303 assert!(css_classes.contains("decoration-4"));
1304
1305 assert!(css_classes.contains("underline-offset-auto"));
1307 assert!(css_classes.contains("underline-offset-0"));
1308 assert!(css_classes.contains("underline-offset-1"));
1309 assert!(css_classes.contains("underline-offset-2"));
1310 assert!(css_classes.contains("underline-offset-4"));
1311 }
1312
1313 #[test]
1315 fn test_comprehensive_extended_typography() {
1316 let classes = ClassBuilder::new()
1317 .line_height(LineHeight::Three)
1319 .line_height(LineHeight::Ten)
1320 .text_decoration(TextDecoration::Underline)
1322 .text_decoration_style(TextDecorationStyle::Wavy)
1323 .text_decoration_thickness(TextDecorationThickness::Two)
1324 .text_underline_offset(TextUnderlineOffset::Four)
1325 .font_size(FontSize::Xl2)
1327 .font_weight(FontWeight::Bold)
1328 .text_transform(TextTransform::Uppercase)
1329 .letter_spacing(LetterSpacing::Wide)
1330 .build();
1331
1332 let css_classes = classes.to_css_classes();
1333
1334 assert!(css_classes.contains("leading-3"));
1336 assert!(css_classes.contains("leading-10"));
1337
1338 assert!(css_classes.contains("underline"));
1340 assert!(css_classes.contains("decoration-wavy"));
1341 assert!(css_classes.contains("decoration-2"));
1342 assert!(css_classes.contains("underline-offset-4"));
1343
1344 assert!(css_classes.contains("text-2xl"));
1346 assert!(css_classes.contains("font-bold"));
1347 assert!(css_classes.contains("uppercase"));
1348 assert!(css_classes.contains("tracking-wide"));
1349 }
1350
1351 #[test]
1353 fn test_line_height_all_values() {
1354 let all_values = LineHeight::all_values();
1355 assert_eq!(all_values.len(), 14); assert!(all_values.contains(&LineHeight::None));
1359 assert!(all_values.contains(&LineHeight::Three));
1360 assert!(all_values.contains(&LineHeight::Four));
1361 assert!(all_values.contains(&LineHeight::Five));
1362 assert!(all_values.contains(&LineHeight::Six));
1363 assert!(all_values.contains(&LineHeight::Seven));
1364 assert!(all_values.contains(&LineHeight::Eight));
1365 assert!(all_values.contains(&LineHeight::Nine));
1366 assert!(all_values.contains(&LineHeight::Ten));
1367 assert!(all_values.contains(&LineHeight::Tight));
1368 assert!(all_values.contains(&LineHeight::Snug));
1369 assert!(all_values.contains(&LineHeight::Normal));
1370 assert!(all_values.contains(&LineHeight::Relaxed));
1371 assert!(all_values.contains(&LineHeight::Loose));
1372 }
1373
1374 #[test]
1375 fn test_font_family_display() {
1376 assert_eq!(format!("{}", FontFamily::Sans), "sans");
1378 assert_eq!(format!("{}", FontFamily::Serif), "serif");
1379 assert_eq!(format!("{}", FontFamily::Mono), "mono");
1380 }
1381
1382 #[test]
1383 fn test_font_size_display() {
1384 assert_eq!(format!("{}", FontSize::Xs), "xs");
1386 assert_eq!(format!("{}", FontSize::Sm), "sm");
1387 assert_eq!(format!("{}", FontSize::Base), "base");
1388 assert_eq!(format!("{}", FontSize::Lg), "lg");
1389 assert_eq!(format!("{}", FontSize::Xl), "xl");
1390 assert_eq!(format!("{}", FontSize::Xl2), "2xl");
1391 assert_eq!(format!("{}", FontSize::Xl3), "3xl");
1392 assert_eq!(format!("{}", FontSize::Xl4), "4xl");
1393 assert_eq!(format!("{}", FontSize::Xl5), "5xl");
1394 assert_eq!(format!("{}", FontSize::Xl6), "6xl");
1395 assert_eq!(format!("{}", FontSize::Xl7), "7xl");
1396 assert_eq!(format!("{}", FontSize::Xl8), "8xl");
1397 assert_eq!(format!("{}", FontSize::Xl9), "9xl");
1398 }
1399
1400 #[test]
1401 fn test_font_weight_display() {
1402 assert_eq!(format!("{}", FontWeight::Thin), "thin");
1404 assert_eq!(format!("{}", FontWeight::ExtraLight), "extralight");
1405 assert_eq!(format!("{}", FontWeight::Light), "light");
1406 assert_eq!(format!("{}", FontWeight::Normal), "normal");
1407 assert_eq!(format!("{}", FontWeight::Medium), "medium");
1408 assert_eq!(format!("{}", FontWeight::SemiBold), "semibold");
1409 assert_eq!(format!("{}", FontWeight::Bold), "bold");
1410 assert_eq!(format!("{}", FontWeight::ExtraBold), "extrabold");
1411 assert_eq!(format!("{}", FontWeight::Black), "black");
1412 }
1413
1414 #[test]
1415 fn test_text_align_display() {
1416 assert_eq!(format!("{}", TextAlign::Left), "left");
1418 assert_eq!(format!("{}", TextAlign::Center), "center");
1419 assert_eq!(format!("{}", TextAlign::Right), "right");
1420 assert_eq!(format!("{}", TextAlign::Justify), "justify");
1421 assert_eq!(format!("{}", TextAlign::Start), "start");
1422 assert_eq!(format!("{}", TextAlign::End), "end");
1423 }
1424
1425 #[test]
1426 fn test_line_height_display() {
1427 assert_eq!(format!("{}", LineHeight::None), "none");
1429 assert_eq!(format!("{}", LineHeight::Three), "3");
1430 assert_eq!(format!("{}", LineHeight::Four), "4");
1431 assert_eq!(format!("{}", LineHeight::Five), "5");
1432 assert_eq!(format!("{}", LineHeight::Six), "6");
1433 assert_eq!(format!("{}", LineHeight::Seven), "7");
1434 assert_eq!(format!("{}", LineHeight::Eight), "8");
1435 assert_eq!(format!("{}", LineHeight::Nine), "9");
1436 assert_eq!(format!("{}", LineHeight::Ten), "10");
1437 assert_eq!(format!("{}", LineHeight::Tight), "tight");
1438 assert_eq!(format!("{}", LineHeight::Snug), "snug");
1439 assert_eq!(format!("{}", LineHeight::Normal), "normal");
1440 assert_eq!(format!("{}", LineHeight::Relaxed), "relaxed");
1441 assert_eq!(format!("{}", LineHeight::Loose), "loose");
1442 assert_eq!(format!("{}", LineHeight::Custom(1.5)), "1.5");
1443 }
1444
1445 #[test]
1446 fn test_letter_spacing_display() {
1447 assert_eq!(format!("{}", LetterSpacing::Tighter), "tighter");
1449 assert_eq!(format!("{}", LetterSpacing::Tight), "tight");
1450 assert_eq!(format!("{}", LetterSpacing::Normal), "normal");
1451 assert_eq!(format!("{}", LetterSpacing::Wide), "wide");
1452 assert_eq!(format!("{}", LetterSpacing::Wider), "wider");
1453 assert_eq!(format!("{}", LetterSpacing::Widest), "widest");
1454 assert_eq!(format!("{}", LetterSpacing::Custom(0.1)), "0.1");
1455 }
1456
1457 #[test]
1458 fn test_text_decoration_display() {
1459 assert_eq!(format!("{}", TextDecoration::None), "no-underline");
1461 assert_eq!(format!("{}", TextDecoration::Underline), "underline");
1462 assert_eq!(format!("{}", TextDecoration::Overline), "overline");
1463 assert_eq!(format!("{}", TextDecoration::LineThrough), "line-through");
1464 }
1465
1466 #[test]
1467 fn test_text_transform_display() {
1468 assert_eq!(format!("{}", TextTransform::None), "normal-case");
1470 assert_eq!(format!("{}", TextTransform::Uppercase), "uppercase");
1471 assert_eq!(format!("{}", TextTransform::Lowercase), "lowercase");
1472 assert_eq!(format!("{}", TextTransform::Capitalize), "capitalize");
1473 }
1474
1475 #[test]
1476 fn test_text_overflow_class_names() {
1477 assert_eq!(TextOverflow::Truncate.to_class_name(), "truncate");
1479 assert_eq!(TextOverflow::Ellipsis.to_class_name(), "text-ellipsis");
1480 assert_eq!(TextOverflow::Clip.to_class_name(), "text-clip");
1481 }
1482
1483 #[test]
1484 fn test_white_space_class_names() {
1485 assert_eq!(WhiteSpace::Normal.to_class_name(), "whitespace-normal");
1487 assert_eq!(WhiteSpace::Nowrap.to_class_name(), "whitespace-nowrap");
1488 assert_eq!(WhiteSpace::Pre.to_class_name(), "whitespace-pre");
1489 assert_eq!(WhiteSpace::PreLine.to_class_name(), "whitespace-pre-line");
1490 assert_eq!(WhiteSpace::PreWrap.to_class_name(), "whitespace-pre-wrap");
1491 assert_eq!(WhiteSpace::BreakSpaces.to_class_name(), "whitespace-break-spaces");
1492 }
1493
1494 #[test]
1495 fn test_word_break_class_names() {
1496 assert_eq!(WordBreak::Normal.to_class_name(), "break-normal");
1498 assert_eq!(WordBreak::BreakAll.to_class_name(), "break-all");
1499 assert_eq!(WordBreak::BreakWords.to_class_name(), "break-words");
1500 assert_eq!(WordBreak::KeepAll.to_class_name(), "break-keep");
1501 }
1502
1503 #[test]
1504 fn test_font_family_css_values() {
1505 assert_eq!(FontFamily::Sans.to_css_value(), "ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"");
1507 assert_eq!(FontFamily::Serif.to_css_value(), "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif");
1508 assert_eq!(FontFamily::Mono.to_css_value(), "ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, \"Liberation Mono\", Menlo, monospace");
1509 }
1510
1511 #[test]
1512 fn test_font_size_css_values() {
1513 assert_eq!(FontSize::Xs.to_css_value(), "0.75rem");
1515 assert_eq!(FontSize::Sm.to_css_value(), "0.875rem");
1516 assert_eq!(FontSize::Base.to_css_value(), "1rem");
1517 assert_eq!(FontSize::Lg.to_css_value(), "1.125rem");
1518 assert_eq!(FontSize::Xl.to_css_value(), "1.25rem");
1519 assert_eq!(FontSize::Xl2.to_css_value(), "1.5rem");
1520 assert_eq!(FontSize::Xl3.to_css_value(), "1.875rem");
1521 assert_eq!(FontSize::Xl4.to_css_value(), "2.25rem");
1522 assert_eq!(FontSize::Xl5.to_css_value(), "3rem");
1523 assert_eq!(FontSize::Xl6.to_css_value(), "3.75rem");
1524 assert_eq!(FontSize::Xl7.to_css_value(), "4.5rem");
1525 assert_eq!(FontSize::Xl8.to_css_value(), "6rem");
1526 assert_eq!(FontSize::Xl9.to_css_value(), "8rem");
1527 }
1528
1529 #[test]
1530 fn test_font_weight_css_values() {
1531 assert_eq!(FontWeight::Thin.to_css_value(), "100");
1533 assert_eq!(FontWeight::ExtraLight.to_css_value(), "200");
1534 assert_eq!(FontWeight::Light.to_css_value(), "300");
1535 assert_eq!(FontWeight::Normal.to_css_value(), "400");
1536 assert_eq!(FontWeight::Medium.to_css_value(), "500");
1537 assert_eq!(FontWeight::SemiBold.to_css_value(), "600");
1538 assert_eq!(FontWeight::Bold.to_css_value(), "700");
1539 assert_eq!(FontWeight::ExtraBold.to_css_value(), "800");
1540 assert_eq!(FontWeight::Black.to_css_value(), "900");
1541 }
1542
1543 #[test]
1544 fn test_text_align_css_values() {
1545 assert_eq!(TextAlign::Left.to_css_value(), "left");
1547 assert_eq!(TextAlign::Center.to_css_value(), "center");
1548 assert_eq!(TextAlign::Right.to_css_value(), "right");
1549 assert_eq!(TextAlign::Justify.to_css_value(), "justify");
1550 assert_eq!(TextAlign::Start.to_css_value(), "start");
1551 assert_eq!(TextAlign::End.to_css_value(), "end");
1552 }
1553
1554 #[test]
1555 fn test_line_height_css_values() {
1556 assert_eq!(LineHeight::None.to_css_value(), "1");
1558 assert_eq!(LineHeight::Three.to_css_value(), "0.75rem");
1559 assert_eq!(LineHeight::Four.to_css_value(), "1rem");
1560 assert_eq!(LineHeight::Five.to_css_value(), "1.25rem");
1561 assert_eq!(LineHeight::Six.to_css_value(), "1.5rem");
1562 assert_eq!(LineHeight::Seven.to_css_value(), "1.75rem");
1563 assert_eq!(LineHeight::Eight.to_css_value(), "2rem");
1564 assert_eq!(LineHeight::Nine.to_css_value(), "2.25rem");
1565 assert_eq!(LineHeight::Ten.to_css_value(), "2.5rem");
1566 assert_eq!(LineHeight::Tight.to_css_value(), "1.25");
1567 assert_eq!(LineHeight::Snug.to_css_value(), "1.375");
1568 assert_eq!(LineHeight::Normal.to_css_value(), "1.5");
1569 assert_eq!(LineHeight::Relaxed.to_css_value(), "1.625");
1570 assert_eq!(LineHeight::Loose.to_css_value(), "2");
1571 assert_eq!(LineHeight::Custom(1.5).to_css_value(), "1.5");
1572 }
1573
1574 #[test]
1575 fn test_letter_spacing_css_values() {
1576 assert_eq!(LetterSpacing::Tighter.to_css_value(), "-0.05em");
1578 assert_eq!(LetterSpacing::Tight.to_css_value(), "-0.025em");
1579 assert_eq!(LetterSpacing::Normal.to_css_value(), "0em");
1580 assert_eq!(LetterSpacing::Wide.to_css_value(), "0.025em");
1581 assert_eq!(LetterSpacing::Wider.to_css_value(), "0.05em");
1582 assert_eq!(LetterSpacing::Widest.to_css_value(), "0.1em");
1583 assert_eq!(LetterSpacing::Custom(0.1).to_css_value(), "0.1em");
1584 }
1585
1586 #[test]
1587 fn test_typography_serialization() {
1588 let font_family = FontFamily::Sans;
1590 let serialized = serde_json::to_string(&font_family).unwrap();
1591 let deserialized: FontFamily = serde_json::from_str(&serialized).unwrap();
1592 assert_eq!(font_family, deserialized);
1593
1594 let font_size = FontSize::Lg;
1595 let serialized = serde_json::to_string(&font_size).unwrap();
1596 let deserialized: FontSize = serde_json::from_str(&serialized).unwrap();
1597 assert_eq!(font_size, deserialized);
1598
1599 let font_weight = FontWeight::Bold;
1600 let serialized = serde_json::to_string(&font_weight).unwrap();
1601 let deserialized: FontWeight = serde_json::from_str(&serialized).unwrap();
1602 assert_eq!(font_weight, deserialized);
1603
1604 let text_align = TextAlign::Center;
1605 let serialized = serde_json::to_string(&text_align).unwrap();
1606 let deserialized: TextAlign = serde_json::from_str(&serialized).unwrap();
1607 assert_eq!(text_align, deserialized);
1608
1609 let line_height = LineHeight::Relaxed;
1610 let serialized = serde_json::to_string(&line_height).unwrap();
1611 let deserialized: LineHeight = serde_json::from_str(&serialized).unwrap();
1612 assert_eq!(line_height, deserialized);
1613
1614 let letter_spacing = LetterSpacing::Wide;
1615 let serialized = serde_json::to_string(&letter_spacing).unwrap();
1616 let deserialized: LetterSpacing = serde_json::from_str(&serialized).unwrap();
1617 assert_eq!(letter_spacing, deserialized);
1618 }
1619
1620 #[test]
1621 fn test_typography_equality_and_hash() {
1622 let font_family1 = FontFamily::Sans;
1624 let font_family2 = FontFamily::Sans;
1625 let font_family3 = FontFamily::Serif;
1626
1627 assert_eq!(font_family1, font_family2);
1628 assert_ne!(font_family1, font_family3);
1629
1630 use std::collections::hash_map::DefaultHasher;
1632 use std::hash::{Hash, Hasher};
1633
1634 let mut hasher1 = DefaultHasher::new();
1635 let mut hasher2 = DefaultHasher::new();
1636 font_family1.hash(&mut hasher1);
1637 font_family2.hash(&mut hasher2);
1638 assert_eq!(hasher1.finish(), hasher2.finish());
1639 }
1640
1641 #[test]
1642 fn test_comprehensive_typography_utilities() {
1643 let classes = ClassBuilder::new()
1645 .font_family(FontFamily::Sans)
1647 .font_family(FontFamily::Serif)
1648 .font_family(FontFamily::Mono)
1649
1650 .font_size(FontSize::Xs)
1652 .font_size(FontSize::Sm)
1653 .font_size(FontSize::Base)
1654 .font_size(FontSize::Lg)
1655 .font_size(FontSize::Xl)
1656 .font_size(FontSize::Xl2)
1657 .font_size(FontSize::Xl3)
1658 .font_size(FontSize::Xl4)
1659 .font_size(FontSize::Xl5)
1660 .font_size(FontSize::Xl6)
1661 .font_size(FontSize::Xl7)
1662 .font_size(FontSize::Xl8)
1663 .font_size(FontSize::Xl9)
1664
1665 .font_weight(FontWeight::Thin)
1667 .font_weight(FontWeight::ExtraLight)
1668 .font_weight(FontWeight::Light)
1669 .font_weight(FontWeight::Normal)
1670 .font_weight(FontWeight::Medium)
1671 .font_weight(FontWeight::SemiBold)
1672 .font_weight(FontWeight::Bold)
1673 .font_weight(FontWeight::ExtraBold)
1674 .font_weight(FontWeight::Black)
1675
1676 .text_align(TextAlign::Left)
1678 .text_align(TextAlign::Center)
1679 .text_align(TextAlign::Right)
1680 .text_align(TextAlign::Justify)
1681 .text_align(TextAlign::Start)
1682 .text_align(TextAlign::End)
1683
1684 .line_height(LineHeight::None)
1686 .line_height(LineHeight::Three)
1687 .line_height(LineHeight::Four)
1688 .line_height(LineHeight::Five)
1689 .line_height(LineHeight::Six)
1690 .line_height(LineHeight::Seven)
1691 .line_height(LineHeight::Eight)
1692 .line_height(LineHeight::Nine)
1693 .line_height(LineHeight::Ten)
1694 .line_height(LineHeight::Tight)
1695 .line_height(LineHeight::Snug)
1696 .line_height(LineHeight::Normal)
1697 .line_height(LineHeight::Relaxed)
1698 .line_height(LineHeight::Loose)
1699 .line_height(LineHeight::Custom(1.5))
1700
1701 .letter_spacing(LetterSpacing::Tighter)
1703 .letter_spacing(LetterSpacing::Tight)
1704 .letter_spacing(LetterSpacing::Normal)
1705 .letter_spacing(LetterSpacing::Wide)
1706 .letter_spacing(LetterSpacing::Wider)
1707 .letter_spacing(LetterSpacing::Widest)
1708 .letter_spacing(LetterSpacing::Custom(0.1))
1709
1710 .text_decoration(TextDecoration::None)
1712 .text_decoration(TextDecoration::Underline)
1713 .text_decoration(TextDecoration::Overline)
1714 .text_decoration(TextDecoration::LineThrough)
1715
1716 .text_transform(TextTransform::None)
1718 .text_transform(TextTransform::Uppercase)
1719 .text_transform(TextTransform::Lowercase)
1720 .text_transform(TextTransform::Capitalize)
1721
1722 .text_overflow(TextOverflow::Truncate)
1724 .text_overflow(TextOverflow::Ellipsis)
1725 .text_overflow(TextOverflow::Clip)
1726
1727 .white_space(WhiteSpace::Normal)
1729 .white_space(WhiteSpace::Nowrap)
1730 .white_space(WhiteSpace::Pre)
1731 .white_space(WhiteSpace::PreLine)
1732 .white_space(WhiteSpace::PreWrap)
1733 .white_space(WhiteSpace::BreakSpaces)
1734
1735 .word_break(WordBreak::Normal)
1737 .word_break(WordBreak::BreakAll)
1738 .word_break(WordBreak::BreakWords)
1739 .word_break(WordBreak::KeepAll)
1740
1741 .text_decoration_style(TextDecorationStyle::Solid)
1743 .text_decoration_style(TextDecorationStyle::Double)
1744 .text_decoration_style(TextDecorationStyle::Dotted)
1745 .text_decoration_style(TextDecorationStyle::Dashed)
1746 .text_decoration_style(TextDecorationStyle::Wavy)
1747
1748 .text_decoration_thickness(TextDecorationThickness::Auto)
1750 .text_decoration_thickness(TextDecorationThickness::FromFont)
1751 .text_decoration_thickness(TextDecorationThickness::Zero)
1752 .text_decoration_thickness(TextDecorationThickness::One)
1753 .text_decoration_thickness(TextDecorationThickness::Two)
1754 .text_decoration_thickness(TextDecorationThickness::Four)
1755 .text_decoration_thickness(TextDecorationThickness::Eight)
1756
1757 .text_underline_offset(TextUnderlineOffset::Auto)
1759 .text_underline_offset(TextUnderlineOffset::Zero)
1760 .text_underline_offset(TextUnderlineOffset::One)
1761 .text_underline_offset(TextUnderlineOffset::Two)
1762 .text_underline_offset(TextUnderlineOffset::Four)
1763 .text_underline_offset(TextUnderlineOffset::Eight)
1764 .build();
1765
1766 let css_classes = classes.to_css_classes();
1767
1768 assert!(css_classes.contains("font-sans"));
1770 assert!(css_classes.contains("font-serif"));
1771 assert!(css_classes.contains("font-mono"));
1772
1773 assert!(css_classes.contains("text-xs"));
1775 assert!(css_classes.contains("text-sm"));
1776 assert!(css_classes.contains("text-base"));
1777 assert!(css_classes.contains("text-lg"));
1778 assert!(css_classes.contains("text-xl"));
1779 assert!(css_classes.contains("text-2xl"));
1780 assert!(css_classes.contains("text-3xl"));
1781 assert!(css_classes.contains("text-4xl"));
1782 assert!(css_classes.contains("text-5xl"));
1783 assert!(css_classes.contains("text-6xl"));
1784 assert!(css_classes.contains("text-7xl"));
1785 assert!(css_classes.contains("text-8xl"));
1786 assert!(css_classes.contains("text-9xl"));
1787
1788 assert!(css_classes.contains("font-thin"));
1790 assert!(css_classes.contains("font-extralight"));
1791 assert!(css_classes.contains("font-light"));
1792 assert!(css_classes.contains("font-normal"));
1793 assert!(css_classes.contains("font-medium"));
1794 assert!(css_classes.contains("font-semibold"));
1795 assert!(css_classes.contains("font-bold"));
1796 assert!(css_classes.contains("font-extrabold"));
1797 assert!(css_classes.contains("font-black"));
1798
1799 assert!(css_classes.contains("text-left"));
1801 assert!(css_classes.contains("text-center"));
1802 assert!(css_classes.contains("text-right"));
1803 assert!(css_classes.contains("text-justify"));
1804 assert!(css_classes.contains("text-start"));
1805 assert!(css_classes.contains("text-end"));
1806
1807 assert!(css_classes.contains("leading-none"));
1809 assert!(css_classes.contains("leading-3"));
1810 assert!(css_classes.contains("leading-4"));
1811 assert!(css_classes.contains("leading-5"));
1812 assert!(css_classes.contains("leading-6"));
1813 assert!(css_classes.contains("leading-7"));
1814 assert!(css_classes.contains("leading-8"));
1815 assert!(css_classes.contains("leading-9"));
1816 assert!(css_classes.contains("leading-10"));
1817 assert!(css_classes.contains("leading-tight"));
1818 assert!(css_classes.contains("leading-snug"));
1819 assert!(css_classes.contains("leading-normal"));
1820 assert!(css_classes.contains("leading-relaxed"));
1821 assert!(css_classes.contains("leading-loose"));
1822 assert!(css_classes.contains("leading-1.5"));
1823
1824 assert!(css_classes.contains("tracking-tighter"));
1826 assert!(css_classes.contains("tracking-tight"));
1827 assert!(css_classes.contains("tracking-normal"));
1828 assert!(css_classes.contains("tracking-wide"));
1829 assert!(css_classes.contains("tracking-wider"));
1830 assert!(css_classes.contains("tracking-widest"));
1831 assert!(css_classes.contains("tracking-0.1"));
1832
1833 assert!(css_classes.contains("no-underline"));
1835 assert!(css_classes.contains("underline"));
1836 assert!(css_classes.contains("overline"));
1837 assert!(css_classes.contains("line-through"));
1838
1839 assert!(css_classes.contains("normal-case"));
1841 assert!(css_classes.contains("uppercase"));
1842 assert!(css_classes.contains("lowercase"));
1843 assert!(css_classes.contains("capitalize"));
1844
1845 assert!(css_classes.contains("truncate"));
1847 assert!(css_classes.contains("text-ellipsis"));
1848 assert!(css_classes.contains("text-clip"));
1849
1850 assert!(css_classes.contains("whitespace-normal"));
1852 assert!(css_classes.contains("whitespace-nowrap"));
1853 assert!(css_classes.contains("whitespace-pre"));
1854 assert!(css_classes.contains("whitespace-pre-line"));
1855 assert!(css_classes.contains("whitespace-pre-wrap"));
1856 assert!(css_classes.contains("whitespace-break-spaces"));
1857
1858 assert!(css_classes.contains("break-normal"));
1860 assert!(css_classes.contains("break-all"));
1861 assert!(css_classes.contains("break-words"));
1862 assert!(css_classes.contains("break-keep"));
1863
1864 assert!(css_classes.contains("decoration-solid"));
1866 assert!(css_classes.contains("decoration-double"));
1867 assert!(css_classes.contains("decoration-dotted"));
1868 assert!(css_classes.contains("decoration-dashed"));
1869 assert!(css_classes.contains("decoration-wavy"));
1870
1871 assert!(css_classes.contains("decoration-auto"));
1873 assert!(css_classes.contains("decoration-from-font"));
1874 assert!(css_classes.contains("decoration-0"));
1875 assert!(css_classes.contains("decoration-1"));
1876 assert!(css_classes.contains("decoration-2"));
1877 assert!(css_classes.contains("decoration-4"));
1878 assert!(css_classes.contains("decoration-8"));
1879
1880 assert!(css_classes.contains("underline-offset-auto"));
1882 assert!(css_classes.contains("underline-offset-0"));
1883 assert!(css_classes.contains("underline-offset-1"));
1884 assert!(css_classes.contains("underline-offset-2"));
1885 assert!(css_classes.contains("underline-offset-4"));
1886 assert!(css_classes.contains("underline-offset-8"));
1887 }
1888}