tailwind_rs_core/utilities/
typography.rs

1//! Typography utilities for tailwind-rs
2//!
3//! This module provides utilities for font families, font sizes, font weights,
4//! text alignment, line height, letter spacing, and other typography-related properties.
5
6use crate::classes::ClassBuilder;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10/// Font family values
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub enum FontFamily {
13    /// Sans-serif font family
14    Sans,
15    /// Serif font family
16    Serif,
17    /// Monospace font family
18    Mono,
19}
20
21/// Font size values
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub enum FontSize {
24    /// Extra small font size
25    Xs,
26    /// Small font size
27    Sm,
28    /// Base font size
29    Base,
30    /// Large font size
31    Lg,
32    /// Extra large font size
33    Xl,
34    /// 2x large font size
35    Xl2,
36    /// 3x large font size
37    Xl3,
38    /// 4x large font size
39    Xl4,
40    /// 5x large font size
41    Xl5,
42    /// 6x large font size
43    Xl6,
44    /// 7x large font size
45    Xl7,
46    /// 8x large font size
47    Xl8,
48    /// 9x large font size
49    Xl9,
50}
51
52/// Font weight values
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub enum FontWeight {
55    /// Thin font weight (100)
56    Thin,
57    /// Extra light font weight (200)
58    ExtraLight,
59    /// Light font weight (300)
60    Light,
61    /// Normal font weight (400)
62    Normal,
63    /// Medium font weight (500)
64    Medium,
65    /// Semi-bold font weight (600)
66    SemiBold,
67    /// Bold font weight (700)
68    Bold,
69    /// Extra bold font weight (800)
70    ExtraBold,
71    /// Black font weight (900)
72    Black,
73}
74
75/// Text alignment values
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
77pub enum TextAlign {
78    /// Left align
79    Left,
80    /// Center align
81    Center,
82    /// Right align
83    Right,
84    /// Justify align
85    Justify,
86    /// Start align (left in LTR, right in RTL)
87    Start,
88    /// End align (right in LTR, left in RTL)
89    End,
90}
91
92/// Line height values
93#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
94pub enum LineHeight {
95    /// None line height
96    None,
97    /// Tight line height
98    Tight,
99    /// Snug line height
100    Snug,
101    /// Normal line height
102    Normal,
103    /// Relaxed line height
104    Relaxed,
105    /// Loose line height
106    Loose,
107    /// Custom line height (f32)
108    Custom(f32),
109}
110
111/// Letter spacing values
112#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
113pub enum LetterSpacing {
114    /// Tighter letter spacing
115    Tighter,
116    /// Tight letter spacing
117    Tight,
118    /// Normal letter spacing
119    Normal,
120    /// Wide letter spacing
121    Wide,
122    /// Wider letter spacing
123    Wider,
124    /// Widest letter spacing
125    Widest,
126    /// Custom letter spacing (f32)
127    Custom(f32),
128}
129
130/// Text decoration values
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
132pub enum TextDecoration {
133    /// No decoration
134    None,
135    /// Underline decoration
136    Underline,
137    /// Overline decoration
138    Overline,
139    /// Line-through decoration
140    LineThrough,
141}
142
143/// Text transform values
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
145pub enum TextTransform {
146    /// No transform
147    None,
148    /// Uppercase transform
149    Uppercase,
150    /// Lowercase transform
151    Lowercase,
152    /// Capitalize transform
153    Capitalize,
154}
155
156/// Text overflow values
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
158pub enum TextOverflow {
159    /// Truncate text overflow
160    Truncate,
161    /// Ellipsis text overflow
162    Ellipsis,
163    /// Clip text overflow
164    Clip,
165}
166
167/// White space values
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
169pub enum WhiteSpace {
170    /// Normal white space
171    Normal,
172    /// Nowrap white space
173    Nowrap,
174    /// Pre white space
175    Pre,
176    /// Pre-line white space
177    PreLine,
178    /// Pre-wrap white space
179    PreWrap,
180    /// Break-spaces white space
181    BreakSpaces,
182}
183
184/// Word break values
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
186pub enum WordBreak {
187    /// Normal word break
188    Normal,
189    /// Break-all word break
190    BreakAll,
191    /// Break-words word break
192    BreakWords,
193    /// Keep-all word break
194    KeepAll,
195}
196
197impl std::hash::Hash for LineHeight {
198    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
199        match self {
200            LineHeight::None => 0u8.hash(state),
201            LineHeight::Tight => 1u8.hash(state),
202            LineHeight::Snug => 2u8.hash(state),
203            LineHeight::Normal => 3u8.hash(state),
204            LineHeight::Relaxed => 4u8.hash(state),
205            LineHeight::Loose => 5u8.hash(state),
206            LineHeight::Custom(f) => {
207                6u8.hash(state);
208                ((f * 1000.0) as u32).hash(state);
209            }
210        }
211    }
212}
213
214impl std::hash::Hash for LetterSpacing {
215    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
216        match self {
217            LetterSpacing::Tighter => 0u8.hash(state),
218            LetterSpacing::Tight => 1u8.hash(state),
219            LetterSpacing::Normal => 2u8.hash(state),
220            LetterSpacing::Wide => 3u8.hash(state),
221            LetterSpacing::Wider => 4u8.hash(state),
222            LetterSpacing::Widest => 5u8.hash(state),
223            LetterSpacing::Custom(f) => {
224                6u8.hash(state);
225                ((f * 1000.0) as u32).hash(state);
226            }
227        }
228    }
229}
230
231impl std::cmp::Eq for LineHeight {}
232impl std::cmp::Eq for LetterSpacing {}
233
234impl FontFamily {
235    pub fn to_class_name(&self) -> String {
236        match self {
237            FontFamily::Sans => "sans".to_string(),
238            FontFamily::Serif => "serif".to_string(),
239            FontFamily::Mono => "mono".to_string(),
240        }
241    }
242    
243    pub fn to_css_value(&self) -> String {
244        match self {
245            FontFamily::Sans => "ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"".to_string(),
246            FontFamily::Serif => "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif".to_string(),
247            FontFamily::Mono => "ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, \"Liberation Mono\", Menlo, monospace".to_string(),
248        }
249    }
250}
251
252impl FontSize {
253    pub fn to_class_name(&self) -> String {
254        match self {
255            FontSize::Xs => "xs".to_string(),
256            FontSize::Sm => "sm".to_string(),
257            FontSize::Base => "base".to_string(),
258            FontSize::Lg => "lg".to_string(),
259            FontSize::Xl => "xl".to_string(),
260            FontSize::Xl2 => "2xl".to_string(),
261            FontSize::Xl3 => "3xl".to_string(),
262            FontSize::Xl4 => "4xl".to_string(),
263            FontSize::Xl5 => "5xl".to_string(),
264            FontSize::Xl6 => "6xl".to_string(),
265            FontSize::Xl7 => "7xl".to_string(),
266            FontSize::Xl8 => "8xl".to_string(),
267            FontSize::Xl9 => "9xl".to_string(),
268        }
269    }
270    
271    pub fn to_css_value(&self) -> String {
272        match self {
273            FontSize::Xs => "0.75rem".to_string(),
274            FontSize::Sm => "0.875rem".to_string(),
275            FontSize::Base => "1rem".to_string(),
276            FontSize::Lg => "1.125rem".to_string(),
277            FontSize::Xl => "1.25rem".to_string(),
278            FontSize::Xl2 => "1.5rem".to_string(),
279            FontSize::Xl3 => "1.875rem".to_string(),
280            FontSize::Xl4 => "2.25rem".to_string(),
281            FontSize::Xl5 => "3rem".to_string(),
282            FontSize::Xl6 => "3.75rem".to_string(),
283            FontSize::Xl7 => "4.5rem".to_string(),
284            FontSize::Xl8 => "6rem".to_string(),
285            FontSize::Xl9 => "8rem".to_string(),
286        }
287    }
288}
289
290impl FontWeight {
291    pub fn to_class_name(&self) -> String {
292        match self {
293            FontWeight::Thin => "thin".to_string(),
294            FontWeight::ExtraLight => "extralight".to_string(),
295            FontWeight::Light => "light".to_string(),
296            FontWeight::Normal => "normal".to_string(),
297            FontWeight::Medium => "medium".to_string(),
298            FontWeight::SemiBold => "semibold".to_string(),
299            FontWeight::Bold => "bold".to_string(),
300            FontWeight::ExtraBold => "extrabold".to_string(),
301            FontWeight::Black => "black".to_string(),
302        }
303    }
304    
305    pub fn to_css_value(&self) -> String {
306        match self {
307            FontWeight::Thin => "100".to_string(),
308            FontWeight::ExtraLight => "200".to_string(),
309            FontWeight::Light => "300".to_string(),
310            FontWeight::Normal => "400".to_string(),
311            FontWeight::Medium => "500".to_string(),
312            FontWeight::SemiBold => "600".to_string(),
313            FontWeight::Bold => "700".to_string(),
314            FontWeight::ExtraBold => "800".to_string(),
315            FontWeight::Black => "900".to_string(),
316        }
317    }
318}
319
320impl TextAlign {
321    pub fn to_class_name(&self) -> String {
322        match self {
323            TextAlign::Left => "left".to_string(),
324            TextAlign::Center => "center".to_string(),
325            TextAlign::Right => "right".to_string(),
326            TextAlign::Justify => "justify".to_string(),
327            TextAlign::Start => "start".to_string(),
328            TextAlign::End => "end".to_string(),
329        }
330    }
331    
332    pub fn to_css_value(&self) -> String {
333        match self {
334            TextAlign::Left => "left".to_string(),
335            TextAlign::Center => "center".to_string(),
336            TextAlign::Right => "right".to_string(),
337            TextAlign::Justify => "justify".to_string(),
338            TextAlign::Start => "start".to_string(),
339            TextAlign::End => "end".to_string(),
340        }
341    }
342}
343
344impl LineHeight {
345    pub fn to_class_name(&self) -> String {
346        match self {
347            LineHeight::None => "none".to_string(),
348            LineHeight::Tight => "tight".to_string(),
349            LineHeight::Snug => "snug".to_string(),
350            LineHeight::Normal => "normal".to_string(),
351            LineHeight::Relaxed => "relaxed".to_string(),
352            LineHeight::Loose => "loose".to_string(),
353            LineHeight::Custom(f) => format!("{}", f),
354        }
355    }
356    
357    pub fn to_css_value(&self) -> String {
358        match self {
359            LineHeight::None => "1".to_string(),
360            LineHeight::Tight => "1.25".to_string(),
361            LineHeight::Snug => "1.375".to_string(),
362            LineHeight::Normal => "1.5".to_string(),
363            LineHeight::Relaxed => "1.625".to_string(),
364            LineHeight::Loose => "2".to_string(),
365            LineHeight::Custom(f) => f.to_string(),
366        }
367    }
368}
369
370impl LetterSpacing {
371    pub fn to_class_name(&self) -> String {
372        match self {
373            LetterSpacing::Tighter => "tighter".to_string(),
374            LetterSpacing::Tight => "tight".to_string(),
375            LetterSpacing::Normal => "normal".to_string(),
376            LetterSpacing::Wide => "wide".to_string(),
377            LetterSpacing::Wider => "wider".to_string(),
378            LetterSpacing::Widest => "widest".to_string(),
379            LetterSpacing::Custom(f) => format!("{}", f),
380        }
381    }
382    
383    pub fn to_css_value(&self) -> String {
384        match self {
385            LetterSpacing::Tighter => "-0.05em".to_string(),
386            LetterSpacing::Tight => "-0.025em".to_string(),
387            LetterSpacing::Normal => "0em".to_string(),
388            LetterSpacing::Wide => "0.025em".to_string(),
389            LetterSpacing::Wider => "0.05em".to_string(),
390            LetterSpacing::Widest => "0.1em".to_string(),
391            LetterSpacing::Custom(f) => format!("{}em", f),
392        }
393    }
394}
395
396impl TextDecoration {
397    pub fn to_class_name(&self) -> String {
398        match self {
399            TextDecoration::None => "no-underline".to_string(),
400            TextDecoration::Underline => "underline".to_string(),
401            TextDecoration::Overline => "overline".to_string(),
402            TextDecoration::LineThrough => "line-through".to_string(),
403        }
404    }
405    
406    pub fn to_css_value(&self) -> String {
407        match self {
408            TextDecoration::None => "none".to_string(),
409            TextDecoration::Underline => "underline".to_string(),
410            TextDecoration::Overline => "overline".to_string(),
411            TextDecoration::LineThrough => "line-through".to_string(),
412        }
413    }
414}
415
416impl TextTransform {
417    pub fn to_class_name(&self) -> String {
418        match self {
419            TextTransform::None => "normal-case".to_string(),
420            TextTransform::Uppercase => "uppercase".to_string(),
421            TextTransform::Lowercase => "lowercase".to_string(),
422            TextTransform::Capitalize => "capitalize".to_string(),
423        }
424    }
425    
426    pub fn to_css_value(&self) -> String {
427        match self {
428            TextTransform::None => "none".to_string(),
429            TextTransform::Uppercase => "uppercase".to_string(),
430            TextTransform::Lowercase => "lowercase".to_string(),
431            TextTransform::Capitalize => "capitalize".to_string(),
432        }
433    }
434}
435
436impl TextOverflow {
437    pub fn to_class_name(&self) -> String {
438        match self {
439            TextOverflow::Truncate => "truncate".to_string(),
440            TextOverflow::Ellipsis => "text-ellipsis".to_string(),
441            TextOverflow::Clip => "text-clip".to_string(),
442        }
443    }
444    
445    pub fn to_css_value(&self) -> String {
446        match self {
447            TextOverflow::Truncate => "truncate".to_string(),
448            TextOverflow::Ellipsis => "ellipsis".to_string(),
449            TextOverflow::Clip => "clip".to_string(),
450        }
451    }
452}
453
454impl WhiteSpace {
455    pub fn to_class_name(&self) -> String {
456        match self {
457            WhiteSpace::Normal => "whitespace-normal".to_string(),
458            WhiteSpace::Nowrap => "whitespace-nowrap".to_string(),
459            WhiteSpace::Pre => "whitespace-pre".to_string(),
460            WhiteSpace::PreLine => "whitespace-pre-line".to_string(),
461            WhiteSpace::PreWrap => "whitespace-pre-wrap".to_string(),
462            WhiteSpace::BreakSpaces => "whitespace-break-spaces".to_string(),
463        }
464    }
465    
466    pub fn to_css_value(&self) -> String {
467        match self {
468            WhiteSpace::Normal => "normal".to_string(),
469            WhiteSpace::Nowrap => "nowrap".to_string(),
470            WhiteSpace::Pre => "pre".to_string(),
471            WhiteSpace::PreLine => "pre-line".to_string(),
472            WhiteSpace::PreWrap => "pre-wrap".to_string(),
473            WhiteSpace::BreakSpaces => "break-spaces".to_string(),
474        }
475    }
476}
477
478impl WordBreak {
479    pub fn to_class_name(&self) -> String {
480        match self {
481            WordBreak::Normal => "break-normal".to_string(),
482            WordBreak::BreakAll => "break-all".to_string(),
483            WordBreak::BreakWords => "break-words".to_string(),
484            WordBreak::KeepAll => "break-keep".to_string(),
485        }
486    }
487    
488    pub fn to_css_value(&self) -> String {
489        match self {
490            WordBreak::Normal => "normal".to_string(),
491            WordBreak::BreakAll => "break-all".to_string(),
492            WordBreak::BreakWords => "break-words".to_string(),
493            WordBreak::KeepAll => "keep-all".to_string(),
494        }
495    }
496}
497
498impl fmt::Display for FontFamily {
499    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500        write!(f, "{}", self.to_class_name())
501    }
502}
503
504impl fmt::Display for FontSize {
505    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506        write!(f, "{}", self.to_class_name())
507    }
508}
509
510impl fmt::Display for FontWeight {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        write!(f, "{}", self.to_class_name())
513    }
514}
515
516impl fmt::Display for TextAlign {
517    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518        write!(f, "{}", self.to_class_name())
519    }
520}
521
522impl fmt::Display for LineHeight {
523    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524        write!(f, "{}", self.to_class_name())
525    }
526}
527
528impl fmt::Display for LetterSpacing {
529    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530        write!(f, "{}", self.to_class_name())
531    }
532}
533
534/// Trait for adding font family utilities to a class builder
535pub trait FontFamilyUtilities {
536    /// Set font family
537    fn font_family(self, family: FontFamily) -> Self;
538}
539
540impl FontFamilyUtilities for ClassBuilder {
541    fn font_family(self, family: FontFamily) -> Self {
542        self.class(format!("font-{}", family.to_class_name()))
543    }
544}
545
546/// Trait for adding font size utilities to a class builder
547pub trait FontSizeUtilities {
548    /// Set font size
549    fn font_size(self, size: FontSize) -> Self;
550}
551
552impl FontSizeUtilities for ClassBuilder {
553    fn font_size(self, size: FontSize) -> Self {
554        self.class(format!("text-{}", size.to_class_name()))
555    }
556}
557
558/// Trait for adding font weight utilities to a class builder
559pub trait FontWeightUtilities {
560    /// Set font weight
561    fn font_weight(self, weight: FontWeight) -> Self;
562}
563
564impl FontWeightUtilities for ClassBuilder {
565    fn font_weight(self, weight: FontWeight) -> Self {
566        self.class(format!("font-{}", weight.to_class_name()))
567    }
568}
569
570/// Trait for adding text alignment utilities to a class builder
571pub trait TextAlignUtilities {
572    /// Set text alignment
573    fn text_align(self, align: TextAlign) -> Self;
574}
575
576impl TextAlignUtilities for ClassBuilder {
577    fn text_align(self, align: TextAlign) -> Self {
578        self.class(format!("text-{}", align.to_class_name()))
579    }
580}
581
582/// Trait for adding line height utilities to a class builder
583pub trait LineHeightUtilities {
584    /// Set line height
585    fn line_height(self, height: LineHeight) -> Self;
586}
587
588impl LineHeightUtilities for ClassBuilder {
589    fn line_height(self, height: LineHeight) -> Self {
590        self.class(format!("leading-{}", height.to_class_name()))
591    }
592}
593
594/// Trait for adding letter spacing utilities to a class builder
595pub trait LetterSpacingUtilities {
596    /// Set letter spacing
597    fn letter_spacing(self, spacing: LetterSpacing) -> Self;
598}
599
600impl LetterSpacingUtilities for ClassBuilder {
601    fn letter_spacing(self, spacing: LetterSpacing) -> Self {
602        self.class(format!("tracking-{}", spacing.to_class_name()))
603    }
604}
605
606/// Trait for adding text decoration utilities to a class builder
607pub trait TextDecorationUtilities {
608    /// Set text decoration
609    fn text_decoration(self, decoration: TextDecoration) -> Self;
610}
611
612impl TextDecorationUtilities for ClassBuilder {
613    fn text_decoration(self, decoration: TextDecoration) -> Self {
614        self.class(decoration.to_class_name())
615    }
616}
617
618/// Trait for adding text transform utilities to a class builder
619pub trait TextTransformUtilities {
620    /// Set text transform
621    fn text_transform(self, transform: TextTransform) -> Self;
622}
623
624impl TextTransformUtilities for ClassBuilder {
625    fn text_transform(self, transform: TextTransform) -> Self {
626        self.class(transform.to_class_name())
627    }
628}
629
630/// Trait for adding text overflow utilities to a class builder
631pub trait TextOverflowUtilities {
632    /// Set text overflow
633    fn text_overflow(self, overflow: TextOverflow) -> Self;
634}
635
636impl TextOverflowUtilities for ClassBuilder {
637    fn text_overflow(self, overflow: TextOverflow) -> Self {
638        self.class(overflow.to_class_name())
639    }
640}
641
642/// Trait for adding white space utilities to a class builder
643pub trait WhiteSpaceUtilities {
644    /// Set white space
645    fn white_space(self, space: WhiteSpace) -> Self;
646}
647
648impl WhiteSpaceUtilities for ClassBuilder {
649    fn white_space(self, space: WhiteSpace) -> Self {
650        self.class(space.to_class_name())
651    }
652}
653
654/// Trait for adding word break utilities to a class builder
655pub trait WordBreakUtilities {
656    /// Set word break
657    fn word_break(self, break_type: WordBreak) -> Self;
658}
659
660impl WordBreakUtilities for ClassBuilder {
661    fn word_break(self, break_type: WordBreak) -> Self {
662        self.class(break_type.to_class_name())
663    }
664}
665
666#[cfg(test)]
667mod tests {
668    use super::*;
669    
670    #[test]
671    fn test_font_family_utilities() {
672        let classes = ClassBuilder::new()
673            .font_family(FontFamily::Sans)
674            .font_family(FontFamily::Serif)
675            .font_family(FontFamily::Mono)
676            .build();
677        
678        let css_classes = classes.to_css_classes();
679        assert!(css_classes.contains("font-sans"));
680        assert!(css_classes.contains("font-serif"));
681        assert!(css_classes.contains("font-mono"));
682    }
683    
684    #[test]
685    fn test_font_size_utilities() {
686        let classes = ClassBuilder::new()
687            .font_size(FontSize::Xs)
688            .font_size(FontSize::Sm)
689            .font_size(FontSize::Base)
690            .font_size(FontSize::Lg)
691            .font_size(FontSize::Xl)
692            .build();
693        
694        let css_classes = classes.to_css_classes();
695        assert!(css_classes.contains("text-xs"));
696        assert!(css_classes.contains("text-sm"));
697        assert!(css_classes.contains("text-base"));
698        assert!(css_classes.contains("text-lg"));
699        assert!(css_classes.contains("text-xl"));
700    }
701    
702    #[test]
703    fn test_font_weight_utilities() {
704        let classes = ClassBuilder::new()
705            .font_weight(FontWeight::Thin)
706            .font_weight(FontWeight::Normal)
707            .font_weight(FontWeight::Bold)
708            .font_weight(FontWeight::Black)
709            .build();
710        
711        let css_classes = classes.to_css_classes();
712        assert!(css_classes.contains("font-thin"));
713        assert!(css_classes.contains("font-normal"));
714        assert!(css_classes.contains("font-bold"));
715        assert!(css_classes.contains("font-black"));
716    }
717    
718    #[test]
719    fn test_text_align_utilities() {
720        let classes = ClassBuilder::new()
721            .text_align(TextAlign::Left)
722            .text_align(TextAlign::Center)
723            .text_align(TextAlign::Right)
724            .text_align(TextAlign::Justify)
725            .build();
726        
727        let css_classes = classes.to_css_classes();
728        assert!(css_classes.contains("text-left"));
729        assert!(css_classes.contains("text-center"));
730        assert!(css_classes.contains("text-right"));
731        assert!(css_classes.contains("text-justify"));
732    }
733    
734    #[test]
735    fn test_line_height_utilities() {
736        let classes = ClassBuilder::new()
737            .line_height(LineHeight::Tight)
738            .line_height(LineHeight::Normal)
739            .line_height(LineHeight::Relaxed)
740            .line_height(LineHeight::Custom(1.75))
741            .build();
742        
743        let css_classes = classes.to_css_classes();
744        assert!(css_classes.contains("leading-tight"));
745        assert!(css_classes.contains("leading-normal"));
746        assert!(css_classes.contains("leading-relaxed"));
747        assert!(css_classes.contains("leading-1.75"));
748    }
749    
750    #[test]
751    fn test_letter_spacing_utilities() {
752        let classes = ClassBuilder::new()
753            .letter_spacing(LetterSpacing::Tight)
754            .letter_spacing(LetterSpacing::Normal)
755            .letter_spacing(LetterSpacing::Wide)
756            .letter_spacing(LetterSpacing::Custom(0.1))
757            .build();
758        
759        let css_classes = classes.to_css_classes();
760        assert!(css_classes.contains("tracking-tight"));
761        assert!(css_classes.contains("tracking-normal"));
762        assert!(css_classes.contains("tracking-wide"));
763        assert!(css_classes.contains("tracking-0.1"));
764    }
765    
766    #[test]
767    fn test_text_decoration_utilities() {
768        let classes = ClassBuilder::new()
769            .text_decoration(TextDecoration::None)
770            .text_decoration(TextDecoration::Underline)
771            .text_decoration(TextDecoration::LineThrough)
772            .build();
773        
774        let css_classes = classes.to_css_classes();
775        assert!(css_classes.contains("no-underline"));
776        assert!(css_classes.contains("underline"));
777        assert!(css_classes.contains("line-through"));
778    }
779    
780    #[test]
781    fn test_text_transform_utilities() {
782        let classes = ClassBuilder::new()
783            .text_transform(TextTransform::None)
784            .text_transform(TextTransform::Uppercase)
785            .text_transform(TextTransform::Lowercase)
786            .text_transform(TextTransform::Capitalize)
787            .build();
788        
789        let css_classes = classes.to_css_classes();
790        assert!(css_classes.contains("normal-case"));
791        assert!(css_classes.contains("uppercase"));
792        assert!(css_classes.contains("lowercase"));
793        assert!(css_classes.contains("capitalize"));
794    }
795    
796    #[test]
797    fn test_text_overflow_utilities() {
798        let classes = ClassBuilder::new()
799            .text_overflow(TextOverflow::Truncate)
800            .text_overflow(TextOverflow::Ellipsis)
801            .text_overflow(TextOverflow::Clip)
802            .build();
803        
804        let css_classes = classes.to_css_classes();
805        assert!(css_classes.contains("truncate"));
806        assert!(css_classes.contains("text-ellipsis"));
807        assert!(css_classes.contains("text-clip"));
808    }
809    
810    #[test]
811    fn test_white_space_utilities() {
812        let classes = ClassBuilder::new()
813            .white_space(WhiteSpace::Normal)
814            .white_space(WhiteSpace::Nowrap)
815            .white_space(WhiteSpace::Pre)
816            .build();
817        
818        let css_classes = classes.to_css_classes();
819        assert!(css_classes.contains("whitespace-normal"));
820        assert!(css_classes.contains("whitespace-nowrap"));
821        assert!(css_classes.contains("whitespace-pre"));
822    }
823    
824    #[test]
825    fn test_word_break_utilities() {
826        let classes = ClassBuilder::new()
827            .word_break(WordBreak::Normal)
828            .word_break(WordBreak::BreakAll)
829            .word_break(WordBreak::BreakWords)
830            .build();
831        
832        let css_classes = classes.to_css_classes();
833        assert!(css_classes.contains("break-normal"));
834        assert!(css_classes.contains("break-all"));
835        assert!(css_classes.contains("break-words"));
836    }
837    
838    #[test]
839    fn test_complex_typography_combination() {
840        let classes = ClassBuilder::new()
841            .font_family(FontFamily::Sans)
842            .font_size(FontSize::Lg)
843            .font_weight(FontWeight::Bold)
844            .text_align(TextAlign::Center)
845            .line_height(LineHeight::Relaxed)
846            .letter_spacing(LetterSpacing::Wide)
847            .text_decoration(TextDecoration::Underline)
848            .text_transform(TextTransform::Uppercase)
849            .build();
850        
851        let css_classes = classes.to_css_classes();
852        assert!(css_classes.contains("font-sans"));
853        assert!(css_classes.contains("text-lg"));
854        assert!(css_classes.contains("font-bold"));
855        assert!(css_classes.contains("text-center"));
856        assert!(css_classes.contains("leading-relaxed"));
857        assert!(css_classes.contains("tracking-wide"));
858        assert!(css_classes.contains("underline"));
859        assert!(css_classes.contains("uppercase"));
860    }
861}