Repository: go-analyze/charts (language: go)

Issue:
Change pie chart color 
Hello,

I try to set color to pie chart serie.

In my case, I need, for exemple, to set color "red" (for error data) and green (for valide data) .

That seems don't work.  I try with:
```
	p, err := charts.PieRender(
		values,
		charts.TitleOptionFunc(charts.TitleOption{
			Text:    "Rainfall vs Evaporation",
			Subtext: "Fake Data",
			Left:    charts.PositionCenter,
		}),
		charts.PaddingOptionFunc(charts.Box{
			Top:    20,
			Right:  20,
			Bottom: 10,
			Left:   20,
		}),
		charts.LegendOptionFunc(charts.LegendOption{
			Orient: charts.OrientVertical,
			Data: []string{
				"tests",
				"failures",
				"errors",
			},
			Left: charts.PositionLeft,
		}),
		charts.PieSeriesShowLabel(),
		func(opt *charts.ChartOption) {
			opt.SeriesList[1].Style.FillColor = drawing.ColorRed
			opt.SeriesList[2].Style.FillColor = drawing.ColorRed
		},
	)
```


Discussion / hints:
Thank you for opening this issue @hypolas.  This fork has been re-evaluating the chart configuration options and the series `Style` has not yet been considered.

Looking at the implementation, it looks like setting the `FillColor` currently only functions on Bar charts.  This could be easily extended to also support Pie Charts, but there ends up being a weird side effect that the Legend does not have a reference to the series (also true for bar charts), and thus it's colors are not updated, for example:
![TestPieChartSeriesStyleFill-actual-2925364196](https://github.com/user-attachments/assets/27ae0c27-95c3-4cf0-9b0e-47059ff5699f)

One goal of this fork is to make themes [more straight forward](https://github.com/go-analyze/charts/pull/4).  We don't require themes to be globally registered (they are passed in with the chart configuration).  Would a suitable alternative be to take a theme you like, and then replace the `SeriesColors` with the colors you want represented?  I still want to make theme mutations easier, it would look something like this:
```
	baseTheme := charts.GetTheme(ThemeVividLight)
	var seriesColors []charts.Color
	for i := range series {
		if red {
			seriesColors = append(seriesColors, drawing.ColorRed)
		} else {
			seriesColors = append(seriesColors, baseTheme.GetSeriesColor(i))
		}
	}
	customTheme := baseTheme.WithSeriesColors(seriesColors)
	// or possibly
	customTheme := baseTheme.WithOptions(ThemeOption{SeriesColors: seriesColors})
```

Then your pie chart would be constructed like this:
```
	p, err := charts.PieRender(
		values,
		charts.ThemeOptionFunc(customTheme),
		charts.TitleOptionFunc(charts.TitleOption{
			Text:    "Rainfall vs Evaporation",
			Subtext: "Fake Data",
			Left:    charts.PositionCenter,
		}),
		charts.PaddingOptionFunc(charts.Box{
			Top:    20,
			Right:  20,
			Bottom: 10,
			Left:   20,
		}),
		charts.LegendOptionFunc(charts.LegendOption{
			Orient: charts.OrientVertical,
			Data: []string{
				"tests",
				"failures",
				"errors",
			},
			Left: charts.PositionLeft,
		}),
		charts.PieSeriesShowLabel(),
	)
```

I believe that having a single method of setting the theme for the series would be more straight forward, but I would appreciate your feedback before we make any changes.  Thank you!

Fix patch:
diff --git a/alias.go b/alias.go
index 58bb68e..29001ca 100644
--- a/alias.go
+++ b/alias.go
@@ -6,8 +6,8 @@ import (
 )
 
 type Box = chartdraw.Box
-type Style = chartdraw.Style
 type Color = drawing.Color
+type FontStyle = chartdraw.FontStyle
 
 var BoxZero = chartdraw.BoxZero
 
diff --git a/axis.go b/axis.go
index 69cc754..b47d192 100644
--- a/axis.go
+++ b/axis.go
@@ -4,7 +4,7 @@ import (
 	"math"
 	"strings"
 
-	"github.com/golang/freetype/truetype"
+	"github.com/go-analyze/charts/chartdraw"
 )
 
 type axisPainter struct {
@@ -41,12 +41,8 @@ type AxisOption struct {
 	TickLength int
 	// LabelMargin specifies the margin value of each label.
 	LabelMargin int
-	// FontSize specifies the font size of each label.
-	FontSize float64
-	// Font is the font used to render each label.
-	Font *truetype.Font
-	// FontColor is the color used for text rendered.
-	FontColor Color
+	// FontStyle specifies the font configuration for each label.
+	FontStyle FontStyle
 	// SplitLineShow, set this to true will show axis split line.
 	SplitLineShow bool
 	// TextRotation are the radians for rotating the label.
@@ -81,14 +77,13 @@ func (a *axisPainter) Render() (Box, error) {
 		strokeWidth = 1
 	}
 
-	font := getPreferredFont(opt.Font, a.p.font)
-	fontColor := opt.FontColor
-	if fontColor.IsZero() {
-		fontColor = theme.GetTextColor()
+	fontStyle := opt.FontStyle
+	fontStyle.Font = getPreferredFont(fontStyle.Font, a.p.font)
+	if fontStyle.FontColor.IsZero() {
+		fontStyle.FontColor = theme.GetTextColor()
 	}
-	fontSize := opt.FontSize
-	if fontSize == 0 {
-		fontSize = defaultFontSize
+	if fontStyle.FontSize == 0 {
+		fontStyle.FontSize = defaultFontSize
 	}
 
 	formatter := opt.Formatter
@@ -106,14 +101,12 @@ func (a *axisPainter) Render() (Box, error) {
 	tickLength := getDefaultInt(opt.TickLength, 5)
 	labelMargin := getDefaultInt(opt.LabelMargin, 5)
 
-	style := Style{
+	style := chartdraw.Style{
 		StrokeColor: theme.GetAxisStrokeColor(),
 		StrokeWidth: strokeWidth,
-		Font:        font,
-		FontColor:   fontColor,
-		FontSize:    fontSize,
+		FontStyle:   fontStyle,
 	}
-	top.SetDrawingStyle(style).OverrideTextStyle(style)
+	top.SetDrawingStyle(style).OverrideFontStyle(style.FontStyle)
 
 	isTextRotation := opt.TextRotation != 0
 
@@ -164,8 +157,8 @@ func (a *axisPainter) Render() (Box, error) {
 	case PositionTop:
 		labelPaddingTop = 0
 		x1 = p.Width()
-		y0 = labelMargin + int(opt.FontSize)
-		ticksPaddingTop = int(opt.FontSize)
+		y0 = labelMargin + int(opt.FontStyle.FontSize)
+		ticksPaddingTop = int(opt.FontStyle.FontSize)
 		y1 = y0
 		orient = OrientHorizontal
 	case PositionLeft:
diff --git a/bar_chart.go b/bar_chart.go
index b4761f0..705d31e 100644
--- a/bar_chart.go
+++ b/bar_chart.go
@@ -111,12 +111,9 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
 
 			h := yRange.getHeight(item.Value)
 			fillColor := seriesColor
-			if !item.Style.FillColor.IsZero() {
-				fillColor = item.Style.FillColor
-			}
 			top := barMaxHeight - h
 
-			seriesPainter.OverrideDrawingStyle(Style{
+			seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 				FillColor: fillColor,
 			})
 			if flagIs(true, opt.RoundedBarCaps) {
@@ -146,28 +143,27 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
 			}
 			y := barMaxHeight - h
 			radians := float64(0)
-			fontColor := series.Label.Color
+			fontStyle := series.Label.FontStyle
 			if series.Label.Position == PositionBottom {
 				y = barMaxHeight
 				radians = -math.Pi / 2
-				if fontColor.IsZero() {
+				if fontStyle.FontColor.IsZero() {
 					if isLightColor(fillColor) {
-						fontColor = defaultLightFontColor
+						fontStyle.FontColor = defaultLightFontColor
 					} else {
-						fontColor = defaultDarkFontColor
+						fontStyle.FontColor = defaultDarkFontColor
 					}
 				}
 			}
 			labelPainter.Add(LabelValue{
-				Index: index,
-				Value: item.Value,
-				X:     x + barWidth>>1,
-				Y:     y,
+				Index:     index,
+				Value:     item.Value,
+				FontStyle: fontStyle,
+				X:         x + barWidth>>1,
+				Y:         y,
 				// rotate
-				Radians:   radians,
-				FontColor: fontColor,
-				Offset:    series.Label.Offset,
-				FontSize:  series.Label.FontSize,
+				Radians: radians,
+				Offset:  series.Label.Offset,
 			})
 		}
 
diff --git a/chart_option.go b/chart_option.go
index 44e080f..915496e 100644
--- a/chart_option.go
+++ b/chart_option.go
@@ -387,7 +387,7 @@ func TableOptionRender(opt TableChartOption) (*Painter, error) {
 		OutputFormat: opt.OutputFormat,
 		Width:        opt.Width,
 		Height:       100, // is only used to calculate the height of the table
-		Font:         opt.Font,
+		Font:         opt.FontStyle.Font,
 	})
 	if err != nil {
 		return nil, err
@@ -399,9 +399,9 @@ func TableOptionRender(opt TableChartOption) (*Painter, error) {
 
 	p, err = NewPainter(PainterOptions{
 		OutputFormat: opt.OutputFormat,
-		Width:        info.Width,
-		Height:       info.Height,
-		Font:         opt.Font,
+		Width:        info.width,
+		Height:       info.height,
+		Font:         opt.FontStyle.Font,
 	})
 	if err != nil {
 		return nil, err
diff --git a/chartdraw/annotation_series.go b/chartdraw/annotation_series.go
index 59e741d..ee8a60b 100644
--- a/chartdraw/annotation_series.go
+++ b/chartdraw/annotation_series.go
@@ -35,10 +35,12 @@ func (as AnnotationSeries) GetYAxis() YAxisType {
 
 func (as AnnotationSeries) annotationStyleDefaults(defaults Style) Style {
 	return Style{
-		FontColor:   DefaultTextColor,
-		Font:        defaults.Font,
+		FontStyle: FontStyle{
+			FontColor: DefaultTextColor,
+			Font:      defaults.Font,
+			FontSize:  DefaultAnnotationFontSize,
+		},
 		FillColor:   DefaultAnnotationFillColor,
-		FontSize:    DefaultAnnotationFontSize,
 		StrokeColor: defaults.StrokeColor,
 		StrokeWidth: defaults.StrokeWidth,
 		Padding:     DefaultAnnotationPadding,
diff --git a/chartdraw/bar_chart.go b/chartdraw/bar_chart.go
index 87af998..534ffcd 100644
--- a/chartdraw/bar_chart.go
+++ b/chartdraw/bar_chart.go
@@ -455,10 +455,12 @@ func (bc BarChart) getTitleFontSize() float64 {
 
 func (bc BarChart) styleDefaultsAxes() Style {
 	return Style{
+		FontStyle: FontStyle{
+			Font:      bc.GetFont(),
+			FontSize:  DefaultAxisFontSize,
+			FontColor: bc.GetColorPalette().TextColor(),
+		},
 		StrokeColor:         bc.GetColorPalette().AxisStrokeColor(),
-		Font:                bc.GetFont(),
-		FontSize:            DefaultAxisFontSize,
-		FontColor:           bc.GetColorPalette().TextColor(),
 		TextHorizontalAlign: TextHorizontalAlignCenter,
 		TextVerticalAlign:   TextVerticalAlignTop,
 		TextWrap:            TextWrapWord,
@@ -467,7 +469,9 @@ func (bc BarChart) styleDefaultsAxes() Style {
 
 func (bc BarChart) styleDefaultsElements() Style {
 	return Style{
-		Font: bc.GetFont(),
+		FontStyle: FontStyle{
+			Font: bc.GetFont(),
+		},
 	}
 }
 
diff --git a/chartdraw/chart.go b/chartdraw/chart.go
index 53d4a72..f303ce8 100644
--- a/chartdraw/chart.go
+++ b/chartdraw/chart.go
@@ -517,19 +517,23 @@ func (c Chart) styleDefaultsCanvas() Style {
 
 func (c Chart) styleDefaultsSeries(seriesIndex int) Style {
 	return Style{
+		FontStyle: FontStyle{
+			Font:     c.GetFont(),
+			FontSize: DefaultFontSize,
+		},
 		DotColor:    c.GetColorPalette().GetSeriesColor(seriesIndex),
 		StrokeColor: c.GetColorPalette().GetSeriesColor(seriesIndex),
 		StrokeWidth: DefaultSeriesLineWidth,
-		Font:        c.GetFont(),
-		FontSize:    DefaultFontSize,
 	}
 }
 
 func (c Chart) styleDefaultsAxes() Style {
 	return Style{
-		Font:        c.GetFont(),
-		FontColor:   c.GetColorPalette().TextColor(),
-		FontSize:    DefaultAxisFontSize,
+		FontStyle: FontStyle{
+			Font:      c.GetFont(),
+			FontColor: c.GetColorPalette().TextColor(),
+			FontSize:  DefaultAxisFontSize,
+		},
 		StrokeColor: c.GetColorPalette().AxisStrokeColor(),
 		StrokeWidth: DefaultAxisLineWidth,
 	}
@@ -537,7 +541,9 @@ func (c Chart) styleDefaultsAxes() Style {
 
 func (c Chart) styleDefaultsElements() Style {
 	return Style{
-		Font: c.GetFont(),
+		FontStyle: FontStyle{
+			Font: c.GetFont(),
+		},
 	}
 }
 
diff --git a/chartdraw/donut_chart.go b/chartdraw/donut_chart.go
index 857eac3..62588a1 100644
--- a/chartdraw/donut_chart.go
+++ b/chartdraw/donut_chart.go
@@ -154,7 +154,10 @@ func (pc DonutChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
 	//making the donut hole
 	v := Value{Value: 100, Label: "center"}
 	styletemp := pc.SliceStyle.InheritFrom(Style{
-		StrokeColor: ColorWhite, StrokeWidth: 4.0, FillColor: ColorWhite, FontColor: ColorWhite, //Font:        pc.GetFont(),//FontSize:    pc.getScaledFontSize(),
+		StrokeColor: ColorWhite, StrokeWidth: 4.0, FillColor: ColorWhite,
+		FontStyle: FontStyle{
+			FontColor: ColorWhite, //Font:        pc.GetFont(),//FontSize:    pc.getScaledFontSize(),
+		},
 	})
 	v.Style.InheritFrom(styletemp).WriteToRenderer(r)
 	r.MoveTo(cx, cy)
@@ -223,12 +226,14 @@ func (pc DonutChart) styleDefaultsCanvas() Style {
 
 func (pc DonutChart) styleDonutChartValue(index int) Style {
 	return pc.SliceStyle.InheritFrom(Style{
+		FontStyle: FontStyle{
+			FontSize:  pc.getScaledFontSize(),
+			FontColor: pc.GetColorPalette().TextColor(),
+			Font:      pc.GetFont(),
+		},
 		StrokeColor: ColorWhite,
 		StrokeWidth: 4.0,
 		FillColor:   pc.GetColorPalette().GetSeriesColor(index),
-		FontSize:    pc.getScaledFontSize(),
-		FontColor:   pc.GetColorPalette().TextColor(),
-		Font:        pc.GetFont(),
 	})
 }
 
@@ -256,15 +261,19 @@ func (pc DonutChart) styleDefaultsBackground() Style {
 
 func (pc DonutChart) styleDefaultsElements() Style {
 	return Style{
-		Font: pc.GetFont(),
+		FontStyle: FontStyle{
+			Font: pc.GetFont(),
+		},
 	}
 }
 
 func (pc DonutChart) styleDefaultsTitle() Style {
 	return pc.TitleStyle.InheritFrom(Style{
-		FontColor:           pc.GetColorPalette().TextColor(),
-		Font:                pc.GetFont(),
-		FontSize:            pc.getTitleFontSize(),
+		FontStyle: FontStyle{
+			FontColor: pc.GetColorPalette().TextColor(),
+			Font:      pc.GetFont(),
+			FontSize:  pc.getTitleFontSize(),
+		},
 		TextHorizontalAlign: TextHorizontalAlignCenter,
 		TextVerticalAlign:   TextVerticalAlignTop,
 		TextWrap:            TextWrapWord,
diff --git a/chartdraw/examples/horizontal_stacked_bar/main.go b/chartdraw/examples/horizontal_stacked_bar/main.go
index 8bad746..1391c7e 100644
--- a/chartdraw/examples/horizontal_stacked_bar/main.go
+++ b/chartdraw/examples/horizontal_stacked_bar/main.go
@@ -43,7 +43,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -52,7 +54,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -61,7 +65,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -70,7 +76,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
@@ -85,7 +93,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -94,7 +104,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -103,7 +115,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -112,7 +126,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
@@ -127,7 +143,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -136,7 +154,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -145,7 +165,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -154,7 +176,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
@@ -169,7 +193,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -178,7 +204,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -187,7 +215,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -196,7 +226,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
diff --git a/chartdraw/examples/stacked_bar_labels/main.go b/chartdraw/examples/stacked_bar_labels/main.go
index 5f7cba9..3637667 100644
--- a/chartdraw/examples/stacked_bar_labels/main.go
+++ b/chartdraw/examples/stacked_bar_labels/main.go
@@ -42,7 +42,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -51,7 +53,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -60,7 +64,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -69,7 +75,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
@@ -84,7 +92,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -93,7 +103,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -102,7 +114,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -111,7 +125,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
@@ -126,7 +142,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -135,7 +153,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -144,7 +164,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -153,7 +175,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
@@ -168,7 +192,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorMariner,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -177,7 +203,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorLightSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -186,7 +214,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorPoloBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 					{
@@ -195,7 +225,9 @@ func main() {
 						Style: chartdraw.Style{
 							StrokeWidth: .01,
 							FillColor:   colorSteelBlue,
-							FontColor:   colorWhite,
+							FontStyle: chartdraw.FontStyle{
+								FontColor: colorWhite,
+							},
 						},
 					},
 				},
diff --git a/chartdraw/examples/text_rotation/main.go b/chartdraw/examples/text_rotation/main.go
index 47b7566..c7e7aa1 100644
--- a/chartdraw/examples/text_rotation/main.go
+++ b/chartdraw/examples/text_rotation/main.go
@@ -14,22 +14,28 @@ func main() {
 	r, _ := chartdraw.PNG(1024, 1024)
 
 	chartdraw.Draw.Text(r, "Test", 64, 64, chartdraw.Style{
-		FontColor: drawing.ColorBlack,
-		FontSize:  18,
-		Font:      f,
+		FontStyle: chartdraw.FontStyle{
+			FontColor: drawing.ColorBlack,
+			FontSize:  18,
+			Font:      f,
+		},
 	})
 
 	chartdraw.Draw.Text(r, "Test", 64, 64, chartdraw.Style{
-		FontColor:           drawing.ColorBlack,
-		FontSize:            18,
-		Font:                f,
+		FontStyle: chartdraw.FontStyle{
+			FontColor: drawing.ColorBlack,
+			FontSize:  18,
+			Font:      f,
+		},
 		TextRotationDegrees: 45.0,
 	})
 
 	tb := chartdraw.Draw.MeasureText(r, "Test", chartdraw.Style{
-		FontColor: drawing.ColorBlack,
-		FontSize:  18,
-		Font:      f,
+		FontStyle: chartdraw.FontStyle{
+			FontColor: drawing.ColorBlack,
+			FontSize:  18,
+			Font:      f,
+		},
 	}).Shift(64, 64)
 
 	tbc := tb.Corners().Rotate(45)
diff --git a/chartdraw/legend.go b/chartdraw/legend.go
index 56a5fab..88404f5 100644
--- a/chartdraw/legend.go
+++ b/chartdraw/legend.go
@@ -8,9 +8,11 @@ import (
 func Legend(c *Chart, userDefaults ...Style) Renderable {
 	return func(r Renderer, cb Box, chartDefaults Style) {
 		legendDefaults := Style{
-			FillColor:   drawing.ColorWhite,
-			FontColor:   DefaultTextColor,
-			FontSize:    8.0,
+			FillColor: drawing.ColorWhite,
+			FontStyle: FontStyle{
+				FontColor: DefaultTextColor,
+				FontSize:  8.0,
+			},
 			StrokeColor: DefaultAxisColor,
 			StrokeWidth: DefaultAxisLineWidth,
 		}
@@ -122,9 +124,11 @@ func Legend(c *Chart, userDefaults ...Style) Renderable {
 func LegendThin(c *Chart, userDefaults ...Style) Renderable {
 	return func(r Renderer, cb Box, chartDefaults Style) {
 		legendDefaults := Style{
-			FillColor:   drawing.ColorWhite,
-			FontColor:   DefaultTextColor,
-			FontSize:    8.0,
+			FillColor: drawing.ColorWhite,
+			FontStyle: FontStyle{
+				FontColor: DefaultTextColor,
+				FontSize:  8.0,
+			},
 			StrokeColor: DefaultAxisColor,
 			StrokeWidth: DefaultAxisLineWidth,
 			Padding: Box{
@@ -220,9 +224,11 @@ func LegendThin(c *Chart, userDefaults ...Style) Renderable {
 func LegendLeft(c *Chart, userDefaults ...Style) Renderable {
 	return func(r Renderer, cb Box, chartDefaults Style) {
 		legendDefaults := Style{
-			FillColor:   drawing.ColorWhite,
-			FontColor:   DefaultTextColor,
-			FontSize:    8.0,
+			FillColor: drawing.ColorWhite,
+			FontStyle: FontStyle{
+				FontColor: DefaultTextColor,
+				FontSize:  8.0,
+			},
 			StrokeColor: DefaultAxisColor,
 			StrokeWidth: DefaultAxisLineWidth,
 		}
diff --git a/chartdraw/pie_chart.go b/chartdraw/pie_chart.go
index ddcf444..5d4284c 100644
--- a/chartdraw/pie_chart.go
+++ b/chartdraw/pie_chart.go
@@ -222,9 +222,11 @@ func (pc PieChart) stylePieChartValue(index int) Style {
 		StrokeColor: ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   pc.GetColorPalette().GetSeriesColor(index),
-		FontSize:    pc.getScaledFontSize(),
-		FontColor:   pc.GetColorPalette().TextColor(),
-		Font:        pc.GetFont(),
+		FontStyle: FontStyle{
+			FontSize:  pc.getScaledFontSize(),
+			FontColor: pc.GetColorPalette().TextColor(),
+			Font:      pc.GetFont(),
+		},
 	})
 }
 
@@ -252,15 +254,19 @@ func (pc PieChart) styleDefaultsBackground() Style {
 
 func (pc PieChart) styleDefaultsElements() Style {
 	return Style{
-		Font: pc.GetFont(),
+		FontStyle: FontStyle{
+			Font: pc.GetFont(),
+		},
 	}
 }
 
 func (pc PieChart) styleDefaultsTitle() Style {
 	return pc.TitleStyle.InheritFrom(Style{
-		FontColor:           pc.GetColorPalette().TextColor(),
-		Font:                pc.GetFont(),
-		FontSize:            pc.getTitleFontSize(),
+		FontStyle: FontStyle{
+			FontColor: pc.GetColorPalette().TextColor(),
+			Font:      pc.GetFont(),
+			FontSize:  pc.getTitleFontSize(),
+		},
 		TextHorizontalAlign: TextHorizontalAlignCenter,
 		TextVerticalAlign:   TextVerticalAlignTop,
 		TextWrap:            TextWrapWord,
diff --git a/chartdraw/raster_renderer.go b/chartdraw/raster_renderer.go
index 0e88627..a91c512 100644
--- a/chartdraw/raster_renderer.go
+++ b/chartdraw/raster_renderer.go
@@ -35,7 +35,11 @@ type rasterRenderer struct {
 }
 
 func (rr *rasterRenderer) ResetStyle() {
-	rr.s = Style{Font: rr.s.Font}
+	rr.s = Style{
+		FontStyle: FontStyle{
+			Font: rr.s.Font,
+		},
+	}
 	rr.ClearTextRotation()
 }
 
diff --git a/chartdraw/stacked_bar_chart.go b/chartdraw/stacked_bar_chart.go
index e4b9773..1e89c36 100644
--- a/chartdraw/stacked_bar_chart.go
+++ b/chartdraw/stacked_bar_chart.go
@@ -560,9 +560,11 @@ func (sbc StackedBarChart) styleDefaultsStackedBarValue(index int) Style {
 		StrokeColor: sbc.GetColorPalette().GetSeriesColor(index),
 		StrokeWidth: 3.0,
 		FillColor:   sbc.GetColorPalette().GetSeriesColor(index),
-		FontSize:    sbc.getScaledFontSize(),
-		FontColor:   sbc.GetColorPalette().TextColor(),
-		Font:        sbc.GetFont(),
+		FontStyle: FontStyle{
+			FontSize:  sbc.getScaledFontSize(),
+			FontColor: sbc.GetColorPalette().TextColor(),
+			Font:      sbc.GetFont(),
+		},
 	}
 }
 
@@ -582,10 +584,12 @@ func (sbc StackedBarChart) getScaledFontSize() float64 {
 
 func (sbc StackedBarChart) styleDefaultsAxes() Style {
 	return Style{
-		StrokeColor:         DefaultAxisColor,
-		Font:                sbc.GetFont(),
-		FontSize:            DefaultAxisFontSize,
-		FontColor:           DefaultAxisColor,
+		StrokeColor: DefaultAxisColor,
+		FontStyle: FontStyle{
+			Font:      sbc.GetFont(),
+			FontSize:  DefaultAxisFontSize,
+			FontColor: DefaultAxisColor,
+		},
 		TextHorizontalAlign: TextHorizontalAlignCenter,
 		TextVerticalAlign:   TextVerticalAlignTop,
 		TextWrap:            TextWrapWord,
@@ -594,10 +598,12 @@ func (sbc StackedBarChart) styleDefaultsAxes() Style {
 
 func (sbc StackedBarChart) styleDefaultsHorizontalAxes() Style {
 	return Style{
-		StrokeColor:         DefaultAxisColor,
-		Font:                sbc.GetFont(),
-		FontSize:            DefaultAxisFontSize,
-		FontColor:           DefaultAxisColor,
+		StrokeColor: DefaultAxisColor,
+		FontStyle: FontStyle{
+			Font:      sbc.GetFont(),
+			FontSize:  DefaultAxisFontSize,
+			FontColor: DefaultAxisColor,
+		},
 		TextHorizontalAlign: TextHorizontalAlignCenter,
 		TextVerticalAlign:   TextVerticalAlignMiddle,
 		TextWrap:            TextWrapWord,
@@ -606,6 +612,8 @@ func (sbc StackedBarChart) styleDefaultsHorizontalAxes() Style {
 
 func (sbc StackedBarChart) styleDefaultsElements() Style {
 	return Style{
-		Font: sbc.GetFont(),
+		FontStyle: FontStyle{
+			Font: sbc.GetFont(),
+		},
 	}
 }
diff --git a/chartdraw/style.go b/chartdraw/style.go
index feb0572..5a46a92 100644
--- a/chartdraw/style.go
+++ b/chartdraw/style.go
@@ -35,15 +35,19 @@ func Shown() Style {
 func StyleTextDefaults() Style {
 	font, _ := GetDefaultFont()
 	return Style{
-		Hidden:    false,
-		Font:      font,
-		FontColor: DefaultTextColor,
-		FontSize:  DefaultTitleFontSize,
+		Hidden: false,
+		FontStyle: FontStyle{
+			Font:      font,
+			FontColor: DefaultTextColor,
+			FontSize:  DefaultTitleFontSize,
+		},
 	}
 }
 
 // Style is a simple style set.
 type Style struct {
+	FontStyle
+
 	Hidden  bool
 	Padding Box
 
@@ -61,10 +65,6 @@ type Style struct {
 
 	FillColor drawing.Color
 
-	FontSize  float64
-	FontColor drawing.Color
-	Font      *truetype.Font
-
 	TextHorizontalAlign TextHorizontalAlign
 	TextVerticalAlign   TextVerticalAlign
 	TextWrap            TextWrap
@@ -72,6 +72,13 @@ type Style struct {
 	TextRotationDegrees float64 //0 is unset or normal
 }
 
+// FontStyle contains the font specific style aspects.
+type FontStyle struct {
+	FontSize  float64
+	FontColor drawing.Color
+	Font      *truetype.Font
+}
+
 // IsZero returns if the object is set or not.
 func (s Style) IsZero() bool {
 	return !s.Hidden &&
@@ -251,7 +258,7 @@ func (s Style) GetStrokeDashArray(defaults ...[]float64) []float64 {
 }
 
 // GetFontSize gets the font size.
-func (s Style) GetFontSize(defaults ...float64) float64 {
+func (s FontStyle) GetFontSize(defaults ...float64) float64 {
 	if s.FontSize == 0 {
 		if len(defaults) > 0 {
 			return defaults[0]
@@ -262,7 +269,7 @@ func (s Style) GetFontSize(defaults ...float64) float64 {
 }
 
 // GetFontColor gets the font size.
-func (s Style) GetFontColor(defaults ...drawing.Color) drawing.Color {
+func (s FontStyle) GetFontColor(defaults ...drawing.Color) drawing.Color {
 	if s.FontColor.IsZero() {
 		if len(defaults) > 0 {
 			return defaults[0]
@@ -273,7 +280,7 @@ func (s Style) GetFontColor(defaults ...drawing.Color) drawing.Color {
 }
 
 // GetFont returns the font face.
-func (s Style) GetFont(defaults ...*truetype.Font) *truetype.Font {
+func (s FontStyle) GetFont(defaults ...*truetype.Font) *truetype.Font {
 	if s.Font == nil {
 		if len(defaults) > 0 {
 			return defaults[0]
@@ -375,8 +382,7 @@ func (s Style) WriteDrawingOptionsToRenderer(r Renderer) {
 }
 
 // WriteTextOptionsToRenderer passes just the text style options to a renderer.
-func (s Style) WriteTextOptionsToRenderer(r Renderer) {
-	r.SetClassName(s.GetClassName())
+func (s FontStyle) WriteTextOptionsToRenderer(r Renderer) {
 	r.SetFont(s.GetFont())
 	r.SetFontColor(s.GetFontColor())
 	r.SetFontSize(s.GetFontSize())
@@ -453,10 +459,12 @@ func (s Style) GetFillAndStrokeOptions() Style {
 // GetTextOptions returns just the text components of the style.
 func (s Style) GetTextOptions() Style {
 	return Style{
-		ClassName:           s.ClassName,
-		FontColor:           s.FontColor,
-		FontSize:            s.FontSize,
-		Font:                s.Font,
+		ClassName: s.ClassName,
+		FontStyle: FontStyle{
+			FontColor: s.FontColor,
+			FontSize:  s.FontSize,
+			Font:      s.Font,
+		},
 		TextHorizontalAlign: s.TextHorizontalAlign,
 		TextVerticalAlign:   s.TextVerticalAlign,
 		TextWrap:            s.TextWrap,
diff --git a/chartdraw/vector_renderer.go b/chartdraw/vector_renderer.go
index d69aebf..cf19e95 100644
--- a/chartdraw/vector_renderer.go
+++ b/chartdraw/vector_renderer.go
@@ -58,7 +58,11 @@ type vectorRenderer struct {
 }
 
 func (vr *vectorRenderer) ResetStyle() {
-	vr.s = &Style{Font: vr.s.Font}
+	vr.s = &Style{
+		FontStyle: FontStyle{
+			Font: vr.s.Font,
+		},
+	}
 	vr.fc = nil
 }
 
diff --git a/charts.go b/charts.go
index 7e1de53..7ed46d6 100644
--- a/charts.go
+++ b/charts.go
@@ -8,7 +8,7 @@ import (
 	"github.com/go-analyze/charts/chartdraw"
 )
 
-const labelFontSize = 10
+const labelFontSize = 10.0
 const smallLabelFontSize = 8
 const defaultDotWidth = 2.0
 const defaultStrokeWidth = 2.0
diff --git a/echarts.go b/echarts.go
index 6cd597c..1ca9684 100644
--- a/echarts.go
+++ b/echarts.go
@@ -38,15 +38,6 @@ type EChartStyle struct {
 	Color string `json:"color"`
 }
 
-func (es *EChartStyle) ToStyle() Style {
-	color := parseColor(es.Color)
-	return Style{
-		FillColor:   color,
-		FontColor:   color,
-		StrokeColor: color,
-	}
-}
-
 type EChartsSeriesDataValue struct {
 	values []float64
 }
@@ -320,7 +311,9 @@ func (esList EChartsSeriesList) ToSeriesList() SeriesList {
 					Max:  item.Max,
 					Min:  item.Min,
 					Label: SeriesLabel{
-						Color:    parseColor(item.Label.Color),
+						FontStyle: FontStyle{
+							FontColor: parseColor(item.Label.Color),
+						},
 						Show:     item.Label.Show,
 						Distance: item.Label.Distance,
 					},
@@ -332,16 +325,16 @@ func (esList EChartsSeriesList) ToSeriesList() SeriesList {
 		for j, dataItem := range item.Data {
 			data[j] = SeriesData{
 				Value: dataItem.Value.First(),
-				Style: dataItem.ItemStyle.ToStyle(),
 			}
 		}
 		seriesList = append(seriesList, Series{
 			Type:       item.Type,
 			Data:       data,
 			YAxisIndex: item.YAxisIndex,
-			Style:      item.ItemStyle.ToStyle(),
 			Label: SeriesLabel{
-				Color:    parseColor(item.Label.Color),
+				FontStyle: FontStyle{
+					FontColor: parseColor(item.Label.Color),
+				},
 				Show:     item.Label.Show,
 				Distance: item.Label.Distance,
 			},
@@ -361,8 +354,10 @@ type EChartsTextStyle struct {
 
 func (et *EChartsTextStyle) ToStyle() chartdraw.Style {
 	s := chartdraw.Style{
-		FontSize:  et.FontSize,
-		FontColor: parseColor(et.Color),
+		FontStyle: chartdraw.FontStyle{
+			FontSize:  et.FontSize,
+			FontColor: parseColor(et.Color),
+		},
 	}
 	if et.FontFamily != "" {
 		s.Font = GetFont(et.FontFamily)
@@ -409,24 +404,30 @@ func (eo *EChartsOption) ToOption() ChartOption {
 		Font:         GetFont(fontFamily),
 		Theme:        GetTheme(eo.Theme),
 		Title: TitleOption{
-			Text:             eo.Title.Text,
-			Subtext:          eo.Title.Subtext,
-			FontColor:        titleTextStyle.FontColor,
-			FontSize:         titleTextStyle.FontSize,
-			SubtextFontSize:  titleSubtextStyle.FontSize,
-			SubtextFontColor: titleSubtextStyle.FontColor,
-			Left:             string(eo.Title.Left),
-			Top:              string(eo.Title.Top),
+			Text:    eo.Title.Text,
+			Subtext: eo.Title.Subtext,
+			FontStyle: FontStyle{
+				FontColor: titleTextStyle.FontColor,
+				FontSize:  titleTextStyle.FontSize,
+			},
+			SubtextFontStyle: FontStyle{
+				FontSize:  titleSubtextStyle.FontSize,
+				FontColor: titleSubtextStyle.FontColor,
+			},
+			Left: string(eo.Title.Left),
+			Top:  string(eo.Title.Top),
 		},
 		Legend: LegendOption{
-			Show:      eo.Legend.Show,
-			FontSize:  legendTextStyle.FontSize,
-			FontColor: legendTextStyle.FontColor,
-			Data:      eo.Legend.Data,
-			Left:      string(eo.Legend.Left),
-			Top:       string(eo.Legend.Top),
-			Align:     eo.Legend.Align,
-			Orient:    eo.Legend.Orient,
+			Show: eo.Legend.Show,
+			FontStyle: FontStyle{
+				FontSize:  legendTextStyle.FontSize,
+				FontColor: legendTextStyle.FontColor,
+			},
+			Data:   eo.Legend.Data,
+			Left:   string(eo.Legend.Left),
+			Top:    string(eo.Legend.Top),
+			Align:  eo.Legend.Align,
+			Orient: eo.Legend.Orient,
 		},
 		RadarIndicators: eo.Radar.Indicator,
 		Width:           eo.Width,
diff --git a/examples/line_chart-1/main.go b/examples/line_chart-1/main.go
index 4196daf..75b8d6f 100644
--- a/examples/line_chart-1/main.go
+++ b/examples/line_chart-1/main.go
@@ -91,7 +91,7 @@ func main() {
 			"Search Engine",
 		}),
 		func(opt *charts.ChartOption) {
-			opt.Title.FontSize = 16
+			opt.Title.FontStyle.FontSize = 16
 			opt.Legend.Padding = charts.Box{
 				Left: 100,
 			}
diff --git a/examples/line_chart-2/main.go b/examples/line_chart-2/main.go
index c3cc1ae..f7c4da7 100644
--- a/examples/line_chart-2/main.go
+++ b/examples/line_chart-2/main.go
@@ -44,21 +44,25 @@ func main() {
 			Right:  12,
 		}),
 		charts.YAxisOptionFunc(charts.YAxisOption{
-			Min:            charts.FloatPointer(0.0), // force min to be zero
-			FontSize:       axisFontSize,
+			Min: charts.FloatPointer(0.0), // force min to be zero
+			FontStyle: charts.FontStyle{
+				FontSize: axisFontSize,
+			},
 			Unit:           10,
 			LabelSkipCount: 1,
 		}),
 		charts.XAxisOptionFunc(charts.XAxisOption{
-			Data:        generateLabels(dataPointCount, "foo "),
-			FontSize:    axisFontSize,
+			Data: generateLabels(dataPointCount, "foo "),
+			FontStyle: charts.FontStyle{
+				FontSize: axisFontSize,
+			},
 			BoundaryGap: charts.True(),
 			LabelCount:  10,
 		}),
 		func(opt *charts.ChartOption) {
 			opt.Legend.Left = "680"
 			opt.Legend.Orient = "vertical"
-			opt.Legend.FontSize = 6
+			opt.Legend.FontStyle.FontSize = 6
 			opt.Title.Left = "center"
 			opt.SymbolShow = charts.False()
 			opt.LineStrokeWidth = 1.6
diff --git a/examples/pie_chart/main.go b/examples/pie_chart/main.go
index 57b64b2..efd05c5 100644
--- a/examples/pie_chart/main.go
+++ b/examples/pie_chart/main.go
@@ -32,11 +32,15 @@ func main() {
 	p, err := charts.PieRender(
 		values,
 		charts.TitleOptionFunc(charts.TitleOption{
-			Text:            "Rainfall vs Evaporation",
-			Subtext:         "(Fake Data)",
-			Left:            charts.PositionCenter,
-			FontSize:        16,
-			SubtextFontSize: 10,
+			Text:    "Rainfall vs Evaporation",
+			Subtext: "(Fake Data)",
+			Left:    charts.PositionCenter,
+			FontStyle: charts.FontStyle{
+				FontSize: 16,
+			},
+			SubtextFontStyle: charts.FontStyle{
+				FontSize: 10,
+			},
 		}),
 		charts.PaddingOptionFunc(charts.Box{
 			Top:    20,
@@ -53,8 +57,10 @@ func main() {
 				"Union Ads",
 				"Video Ads",
 			},
-			Left:     charts.PositionLeft,
-			FontSize: 12,
+			Left: charts.PositionLeft,
+			FontStyle: charts.FontStyle{
+				FontSize: 12,
+			},
 		}),
 		charts.PieSeriesShowLabel(),
 	)
diff --git a/examples/radar_chart/main.go b/examples/radar_chart/main.go
index 3a576fd..d7e17b7 100644
--- a/examples/radar_chart/main.go
+++ b/examples/radar_chart/main.go
@@ -43,8 +43,10 @@ func main() {
 	p, err := charts.RadarRender(
 		values,
 		charts.TitleOptionFunc(charts.TitleOption{
-			Text:     "Basic Radar Chart",
-			FontSize: 16,
+			Text: "Basic Radar Chart",
+			FontStyle: charts.FontStyle{
+				FontSize: 16,
+			},
 		}),
 		charts.LegendOptionFunc(charts.LegendOption{
 			Data: []string{
diff --git a/examples/table/main.go b/examples/table/main.go
index 9b05365..4fcf44a 100644
--- a/examples/table/main.go
+++ b/examples/table/main.go
@@ -86,7 +86,9 @@ func main() {
 		HeaderBackgroundColor: charts.Color{R: 80, G: 80, B: 80, A: 255},
 		RowBackgroundColors:   []charts.Color{bgColor},
 		HeaderFontColor:       drawing.ColorWhite,
-		FontColor:             drawing.ColorWhite,
+		FontStyle: charts.FontStyle{
+			FontColor: drawing.ColorWhite,
+		},
 		Padding: charts.Box{
 			Top:    15,
 			Right:  10,
@@ -115,26 +117,22 @@ func main() {
 			charts.AlignRight,
 			charts.AlignRight,
 		},
-		CellStyle: func(tc charts.TableCell) *charts.Style {
+		CellModifier: func(tc charts.TableCell) charts.TableCell {
 			column := tc.Column
 			if column != 2 {
-				return nil
+				return tc
 			}
 			value, _ := strconv.ParseFloat(strings.Replace(tc.Text, "%", "", 1), 64)
 			if value == 0 {
-				return nil
-			}
-			style := charts.Style{
-				Padding: charts.Box{
-					Bottom: 5,
-				},
+				return tc
 			}
+
 			if value > 0 {
-				style.FillColor = charts.Color{R: 179, G: 53, B: 20, A: 255}
+				tc.FillColor = charts.Color{R: 179, G: 53, B: 20, A: 255}
 			} else {
-				style.FillColor = charts.Color{R: 33, G: 124, B: 50, A: 255}
+				tc.FillColor = charts.Color{R: 33, G: 124, B: 50, A: 255}
 			}
-			return &style
+			return tc
 		},
 	})
 	if err != nil {
diff --git a/examples/web-charts/main.go b/examples/web-charts/main.go
index f1bc46d..9d71f56 100644
--- a/examples/web-charts/main.go
+++ b/examples/web-charts/main.go
@@ -148,8 +148,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 	chartOptions := []charts.ChartOption{
 		{
 			Title: charts.TitleOption{
-				Text:     "Line",
-				FontSize: 18,
+				Text: "Line",
+				FontStyle: charts.FontStyle{
+					FontSize: 18,
+				},
 			},
 			Legend: charts.LegendOption{
 				Data: []string{
@@ -272,8 +274,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		},
 		{
 			Title: charts.TitleOption{
-				Text:     "Line Area",
-				FontSize: 18,
+				Text: "Line Area",
+				FontStyle: charts.FontStyle{
+					FontSize: 18,
+				},
 			},
 			Legend: charts.NewLegendOption([]string{
 				"Email",
@@ -306,8 +310,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		// histogram
 		{
 			Title: charts.TitleOption{
-				Text:     "Bar",
-				FontSize: 18,
+				Text: "Bar",
+				FontStyle: charts.FontStyle{
+					FontSize: 18,
+				},
 			},
 			XAxis: charts.NewXAxisOption([]string{
 				"Mon",
@@ -343,9 +349,6 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 						},
 						{
 							Value: 190,
-							Style: charts.Style{
-								FillColor: charts.Color{R: 169, G: 0, B: 0, A: 255},
-							},
 						},
 						{
 							Value: 230,
@@ -373,8 +376,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		// horizontal bar chart
 		{
 			Title: charts.TitleOption{
-				Text:     "World Population",
-				FontSize: 18,
+				Text: "World Population",
+				FontStyle: charts.FontStyle{
+					FontSize: 18,
+				},
 			},
 			Padding: charts.Box{
 				Top:    20,
@@ -422,10 +427,14 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		// histogram+marker
 		{
 			Title: charts.TitleOption{
-				Text:            "Rainfall vs Evaporation",
-				Subtext:         "(Fake Data)",
-				FontSize:        16,
-				SubtextFontSize: 10,
+				Text:    "Rainfall vs Evaporation",
+				Subtext: "(Fake Data)",
+				FontStyle: charts.FontStyle{
+					FontSize: 16,
+				},
+				SubtextFontStyle: charts.FontStyle{
+					FontSize: 10,
+				},
 			},
 			Padding: charts.Box{
 				Top:    20,
@@ -505,8 +514,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		// dual y-axis
 		{
 			Title: charts.TitleOption{
-				Text:     "Temperature",
-				FontSize: 16,
+				Text: "Temperature",
+				FontStyle: charts.FontStyle{
+					FontSize: 16,
+				},
 			},
 			XAxis: charts.NewXAxisOption([]string{
 				"Jan",
@@ -597,11 +608,15 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		// pie chart
 		{
 			Title: charts.TitleOption{
-				Text:            "Referer of a Website",
-				Subtext:         "(Fake Data)",
-				Left:            charts.PositionCenter,
-				FontSize:        16,
-				SubtextFontSize: 10,
+				Text:    "Referer of a Website",
+				Subtext: "(Fake Data)",
+				Left:    charts.PositionCenter,
+				FontStyle: charts.FontStyle{
+					FontSize: 16,
+				},
+				SubtextFontStyle: charts.FontStyle{
+					FontSize: 10,
+				},
 			},
 			Legend: charts.LegendOption{
 				Orient: charts.OrientVertical,
@@ -630,8 +645,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
 		// radar chart
 		{
 			Title: charts.TitleOption{
-				Text:     "Basic Radar Chart",
-				FontSize: 16,
+				Text: "Basic Radar Chart",
+				FontStyle: charts.FontStyle{
+					FontSize: 16,
+				},
 			},
 			Legend: charts.LegendOption{
 				Data: []string{
diff --git a/funnel_chart.go b/funnel_chart.go
index 48822be..79dec22 100644
--- a/funnel_chart.go
+++ b/funnel_chart.go
@@ -2,6 +2,8 @@ package charts
 
 import (
 	"github.com/golang/freetype/truetype"
+
+	"github.com/go-analyze/charts/chartdraw"
 )
 
 type funnelChart struct {
@@ -120,13 +122,13 @@ func (f *funnelChart) render(result *defaultRenderResult, seriesList SeriesList)
 		}
 		color := theme.GetSeriesColor(series.index)
 
-		seriesPainter.OverrideDrawingStyle(Style{
+		seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 			FillColor: color,
 		}).FillArea(points)
 
 		// text
 		text := textList[index]
-		seriesPainter.OverrideTextStyle(Style{
+		seriesPainter.OverrideFontStyle(FontStyle{
 			FontColor: theme.GetTextColor(),
 			FontSize:  labelFontSize,
 			Font:      opt.Font,
diff --git a/grid.go b/grid.go
index 9519785..2a752af 100644
--- a/grid.go
+++ b/grid.go
@@ -1,5 +1,9 @@
 package charts
 
+import (
+	"github.com/go-analyze/charts/chartdraw"
+)
+
 type gridPainter struct {
 	p   *Painter
 	opt *GridPainterOption
@@ -55,7 +59,7 @@ func (g *gridPainter) Render() (Box, error) {
 		strokeWidth = 1
 	}
 
-	g.p.SetDrawingStyle(Style{
+	g.p.SetDrawingStyle(chartdraw.Style{
 		StrokeWidth: strokeWidth,
 		StrokeColor: opt.StrokeColor,
 	})
diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go
index d2d30b5..3374147 100644
--- a/horizontal_bar_chart.go
+++ b/horizontal_bar_chart.go
@@ -102,11 +102,8 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
 
 			w := xRange.getHeight(item.Value)
 			fillColor := seriesColor
-			if !item.Style.FillColor.IsZero() {
-				fillColor = item.Style.FillColor
-			}
 			right := w
-			seriesPainter.OverrideDrawingStyle(Style{
+			seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 				FillColor: fillColor,
 			}).Rect(chartdraw.Box{
 				Top:    y,
@@ -118,6 +115,14 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
 			if labelPainter == nil {
 				continue
 			}
+			fontStyle := series.Label.FontStyle
+			if fontStyle.FontColor.IsZero() {
+				if isLightColor(fillColor) {
+					fontStyle.FontColor = defaultLightFontColor
+				} else {
+					fontStyle.FontColor = defaultDarkFontColor
+				}
+			}
 			labelValue := LabelValue{
 				Orient:    OrientHorizontal,
 				Index:     index,
@@ -125,18 +130,10 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
 				X:         right,
 				Y:         y + barHeight>>1,
 				Offset:    series.Label.Offset,
-				FontColor: series.Label.Color,
-				FontSize:  series.Label.FontSize,
+				FontStyle: fontStyle,
 			}
 			if series.Label.Position == PositionLeft {
 				labelValue.X = 0
-				if labelValue.FontColor.IsZero() {
-					if isLightColor(fillColor) {
-						labelValue.FontColor = defaultLightFontColor
-					} else {
-						labelValue.FontColor = defaultDarkFontColor
-					}
-				}
 			}
 			labelPainter.Add(labelValue)
 		}
diff --git a/legend.go b/legend.go
index 07bce80..537f32a 100644
--- a/legend.go
+++ b/legend.go
@@ -3,7 +3,7 @@ package charts
 import (
 	"fmt"
 
-	"github.com/golang/freetype/truetype"
+	"github.com/go-analyze/charts/chartdraw"
 )
 
 type legendPainter struct {
@@ -23,12 +23,8 @@ type LegendOption struct {
 	Padding Box
 	// Data provides text for the legend.
 	Data []string
-	// FontSize specifies the font size of each label.
-	FontSize float64
-	// Font is the font used to render each label.
-	Font *truetype.Font
-	// FontColor is the color used for text rendered.
-	FontColor Color
+	// FontStyle specifies the font, size, and style for rendering the legend.
+	FontStyle FontStyle
 	// Left is the distance between legend component and the left side of the container.
 	// It can be pixel value (20), percentage value (20%), or position description: 'left', 'right', 'center'.
 	Left string
@@ -82,11 +78,12 @@ func (l *legendPainter) Render() (Box, error) {
 	if theme == nil {
 		theme = getPreferredTheme(l.p.theme)
 	}
-	if opt.FontSize == 0 {
-		opt.FontSize = defaultFontSize
+	fontStyle := opt.FontStyle
+	if fontStyle.FontSize == 0 {
+		fontStyle.FontSize = defaultFontSize
 	}
-	if opt.FontColor.IsZero() {
-		opt.FontColor = theme.GetTextColor()
+	if fontStyle.FontColor.IsZero() {
+		fontStyle.FontColor = theme.GetTextColor()
 	}
 	if opt.Left == "" { // default a center legend
 		opt.Left = PositionCenter
@@ -96,10 +93,7 @@ func (l *legendPainter) Render() (Box, error) {
 		padding.Top = 5
 	}
 	p := l.p.Child(PainterPaddingOption(padding))
-	p.SetTextStyle(Style{
-		FontSize:  opt.FontSize,
-		FontColor: opt.FontColor,
-	})
+	p.SetFontStyle(fontStyle)
 	measureList := make([]Box, len(opt.Data))
 	maxTextWidth := 0
 	for index, text := range opt.Data {
@@ -196,7 +190,7 @@ func (l *legendPainter) Render() (Box, error) {
 	lastIndex := len(opt.Data) - 1
 	for index, text := range opt.Data {
 		color := theme.GetSeriesColor(index)
-		p.SetDrawingStyle(Style{
+		p.SetDrawingStyle(chartdraw.Style{
 			FillColor:   color,
 			StrokeColor: color,
 		})
diff --git a/line_chart.go b/line_chart.go
index e580593..f066286 100644
--- a/line_chart.go
+++ b/line_chart.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/golang/freetype/truetype"
 
+	"github.com/go-analyze/charts/chartdraw"
 	"github.com/go-analyze/charts/chartdraw/drawing"
 )
 
@@ -88,13 +89,10 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
 	for index := range seriesList {
 		series := seriesList[index]
 		seriesColor := opt.Theme.GetSeriesColor(series.index)
-		drawingStyle := Style{
+		drawingStyle := chartdraw.Style{
 			StrokeColor: seriesColor,
 			StrokeWidth: strokeWidth,
 		}
-		if len(series.Style.StrokeDashArray) > 0 {
-			drawingStyle.StrokeDashArray = series.Style.StrokeDashArray
-		}
 
 		yRange := result.axisRanges[series.YAxisIndex]
 		points := make([]Point, 0)
@@ -125,11 +123,11 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
 				continue
 			}
 			labelPainter.Add(LabelValue{
-				Index:    index,
-				Value:    item.Value,
-				X:        p.X,
-				Y:        p.Y,
-				FontSize: series.Label.FontSize,
+				Index:     index,
+				Value:     item.Value,
+				X:         p.X,
+				Y:         p.Y,
+				FontStyle: series.Label.FontStyle,
 			})
 		}
 		if opt.FillArea {
@@ -147,7 +145,7 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
 				X: areaPoints[0].X,
 				Y: bottomY,
 			}, areaPoints[0])
-			seriesPainter.SetDrawingStyle(Style{
+			seriesPainter.SetDrawingStyle(chartdraw.Style{
 				FillColor: seriesColor.WithAlpha(opacity),
 			})
 			seriesPainter.FillArea(areaPoints)
diff --git a/mark_line.go b/mark_line.go
index 2752fe0..79f66f9 100644
--- a/mark_line.go
+++ b/mark_line.go
@@ -2,6 +2,8 @@ package charts
 
 import (
 	"github.com/golang/freetype/truetype"
+
+	"github.com/go-analyze/charts/chartdraw"
 )
 
 // NewMarkLine returns a series mark line
@@ -57,7 +59,7 @@ func (m *markLinePainter) Render() (Box, error) {
 		summary := s.Summary()
 		for _, markLine := range s.MarkLine.Data {
 			// since the mark line will modify the style, it must be reset every time
-			painter.OverrideDrawingStyle(Style{
+			painter.OverrideDrawingStyle(chartdraw.Style{
 				FillColor:   opt.FillColor,
 				StrokeColor: opt.StrokeColor,
 				StrokeWidth: 1,
@@ -65,7 +67,7 @@ func (m *markLinePainter) Render() (Box, error) {
 					4,
 					2,
 				},
-			}).OverrideTextStyle(Style{
+			}).OverrideFontStyle(FontStyle{
 				Font:      font,
 				FontColor: opt.FontColor,
 				FontSize:  labelFontSize,
diff --git a/mark_point.go b/mark_point.go
index 173d2f1..666b25a 100644
--- a/mark_point.go
+++ b/mark_point.go
@@ -2,6 +2,8 @@ package charts
 
 import (
 	"github.com/golang/freetype/truetype"
+
+	"github.com/go-analyze/charts/chartdraw"
 )
 
 // NewMarkPoint returns a series mark point
@@ -53,22 +55,24 @@ func (m *markPointPainter) Render() (Box, error) {
 		if symbolSize == 0 {
 			symbolSize = 28
 		}
-		textStyle := Style{
-			FontSize:    labelFontSize,
+		textStyle := chartdraw.Style{
+			FontStyle: FontStyle{
+				FontSize: labelFontSize,
+				Font:     opt.Font,
+			},
 			StrokeWidth: 1,
-			Font:        opt.Font,
 		}
 		if isLightColor(opt.FillColor) {
 			textStyle.FontColor = defaultLightFontColor
 		} else {
 			textStyle.FontColor = defaultDarkFontColor
 		}
-		painter.OverrideDrawingStyle(Style{
+		painter.OverrideDrawingStyle(chartdraw.Style{
 			FillColor: opt.FillColor,
-		}).OverrideTextStyle(textStyle)
+		}).OverrideFontStyle(textStyle.FontStyle)
 		for _, markPointData := range opt.Series.MarkPoint.Data {
 			textStyle.FontSize = labelFontSize
-			painter.OverrideTextStyle(textStyle)
+			painter.OverrideFontStyle(textStyle.FontStyle)
 			p := points[summary.MinIndex]
 			value := summary.MinValue
 			switch markPointData.Type {
@@ -82,7 +86,7 @@ func (m *markPointPainter) Render() (Box, error) {
 			textBox := painter.MeasureText(text)
 			if textBox.Width() > symbolSize {
 				textStyle.FontSize = smallLabelFontSize
-				painter.OverrideTextStyle(textStyle)
+				painter.OverrideFontStyle(textStyle.FontStyle)
 				textBox = painter.MeasureText(text)
 			}
 			painter.Text(text, p.X-textBox.Width()>>1, p.Y-symbolSize>>1-2)
diff --git a/painter.go b/painter.go
index 055e033..7e3957c 100644
--- a/painter.go
+++ b/painter.go
@@ -16,7 +16,7 @@ type Painter struct {
 	render         chartdraw.Renderer
 	box            Box
 	parent         *Painter
-	style          Style
+	style          chartdraw.Style
 	theme          ColorPalette
 	font           *truetype.Font
 	outputFormat   string
@@ -102,7 +102,7 @@ func PainterFontOption(font *truetype.Font) PainterOption {
 }
 
 // PainterStyleOption sets the style of draw painter
-func PainterStyleOption(style Style) PainterOption {
+func PainterStyleOption(style chartdraw.Style) PainterOption {
 	return func(p *Painter) {
 		p.SetStyle(style)
 	}
@@ -186,7 +186,7 @@ func (p *Painter) Child(opt ...PainterOption) *Painter {
 	return child
 }
 
-func (p *Painter) SetStyle(style Style) {
+func (p *Painter) SetStyle(style chartdraw.Style) {
 	if style.Font == nil {
 		style.Font = p.font
 	}
@@ -194,7 +194,7 @@ func (p *Painter) SetStyle(style Style) {
 	style.WriteToRenderer(p.render)
 }
 
-func overrideStyle(defaultStyle Style, style Style) Style {
+func overrideStyle(defaultStyle chartdraw.Style, style chartdraw.Style) chartdraw.Style {
 	if style.StrokeWidth == 0 {
 		style.StrokeWidth = defaultStyle.StrokeWidth
 	}
@@ -225,27 +225,33 @@ func overrideStyle(defaultStyle Style, style Style) Style {
 	return style
 }
 
-func (p *Painter) OverrideDrawingStyle(style Style) *Painter {
+func (p *Painter) OverrideDrawingStyle(style chartdraw.Style) *Painter {
 	s := overrideStyle(p.style, style)
 	p.SetDrawingStyle(s)
 	return p
 }
 
-func (p *Painter) SetDrawingStyle(style Style) *Painter {
+func (p *Painter) SetDrawingStyle(style chartdraw.Style) *Painter {
 	style.WriteDrawingOptionsToRenderer(p.render)
 	return p
 }
 
-func (p *Painter) SetTextStyle(style Style) *Painter {
+func (p *Painter) SetFontStyle(style chartdraw.FontStyle) *Painter {
 	if style.Font == nil {
 		style.Font = p.font
 	}
+	if style.FontColor.IsZero() {
+		style.FontColor = p.style.FontColor
+	}
+	if style.FontSize == 0 {
+		style.FontSize = p.style.FontSize
+	}
 	style.WriteTextOptionsToRenderer(p.render)
 	return p
 }
-func (p *Painter) OverrideTextStyle(style Style) *Painter {
-	s := overrideStyle(p.style, style)
-	p.SetTextStyle(s)
+func (p *Painter) OverrideFontStyle(style chartdraw.FontStyle) *Painter {
+	s := overrideStyle(p.style, chartdraw.Style{FontStyle: style})
+	p.SetFontStyle(s.FontStyle)
 	return p
 }
 
@@ -557,7 +563,7 @@ func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) ch
 	style.TextWrap = chartdraw.TextWrapWord
 	r := p.render
 	lines := chartdraw.Text.WrapFit(r, body, width, style)
-	p.SetTextStyle(style)
+	p.SetFontStyle(style.FontStyle)
 	var output chartdraw.Box
 
 	textAlign := ""
diff --git a/pie_chart.go b/pie_chart.go
index 84f77c2..96a9f94 100644
--- a/pie_chart.go
+++ b/pie_chart.go
@@ -199,7 +199,7 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B
 	maxY := 0
 	minY := 0
 	for _, s := range sectors {
-		seriesPainter.OverrideDrawingStyle(Style{
+		seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 			StrokeWidth: 1,
 			StrokeColor: s.color,
 			FillColor:   s.color,
@@ -240,15 +240,15 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B
 		seriesPainter.MoveTo(s.lineBranchX, s.lineBranchY)
 		seriesPainter.LineTo(s.lineEndX, s.lineEndY)
 		seriesPainter.Stroke()
-		textStyle := Style{
+		textStyle := FontStyle{
 			FontColor: theme.GetTextColor(),
 			FontSize:  labelFontSize,
 			Font:      opt.Font,
 		}
-		if !s.series.Label.Color.IsZero() {
-			textStyle.FontColor = s.series.Label.Color
+		if !s.series.Label.FontStyle.FontColor.IsZero() {
+			textStyle.FontColor = s.series.Label.FontStyle.FontColor
 		}
-		seriesPainter.OverrideTextStyle(textStyle)
+		seriesPainter.OverrideFontStyle(textStyle)
 		x, y := s.calculateTextXY(seriesPainter.MeasureText(s.label))
 		seriesPainter.Text(s.label, x, y)
 	}
diff --git a/radar_chart.go b/radar_chart.go
index a6b9c53..0e684fe 100644
--- a/radar_chart.go
+++ b/radar_chart.go
@@ -106,7 +106,7 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList)
 	divideRadius := float64(int(radius / float64(divideCount)))
 	radius = divideRadius * float64(divideCount)
 
-	seriesPainter.OverrideDrawingStyle(Style{
+	seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 		StrokeColor: theme.GetAxisSplitLineColor(),
 		StrokeWidth: 1,
 	})
@@ -120,7 +120,7 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList)
 		seriesPainter.LineTo(p.X, p.Y)
 		seriesPainter.Stroke()
 	}
-	seriesPainter.OverrideTextStyle(Style{
+	seriesPainter.OverrideFontStyle(FontStyle{
 		FontColor: theme.GetTextColor(),
 		FontSize:  labelFontSize,
 		Font:      opt.Font,
@@ -189,7 +189,7 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList)
 			dotFillColor = color
 		}
 		linePoints = append(linePoints, linePoints[0])
-		seriesPainter.OverrideDrawingStyle(Style{
+		seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 			StrokeColor: color,
 			StrokeWidth: defaultStrokeWidth,
 			DotWidth:    defaultDotWidth,
@@ -199,7 +199,7 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList)
 		seriesPainter.LineStroke(linePoints).
 			FillArea(linePoints)
 		dotWith := 2.0
-		seriesPainter.OverrideDrawingStyle(Style{
+		seriesPainter.OverrideDrawingStyle(chartdraw.Style{
 			StrokeWidth: defaultStrokeWidth,
 			StrokeColor: color,
 			FillColor:   dotFillColor,
diff --git a/series.go b/series.go
index 453cc19..26e6124 100644
--- a/series.go
+++ b/series.go
@@ -5,15 +5,11 @@ import (
 	"strings"
 
 	"github.com/dustin/go-humanize"
-
-	"github.com/go-analyze/charts/chartdraw"
 )
 
 type SeriesData struct {
 	// Value is the retained value for the data.
 	Value float64
-	// Style provides a style for the series data.
-	Style Style
 }
 
 // NewSeriesListDataFromValues returns a series list
@@ -53,8 +49,8 @@ type SeriesLabel struct {
 	// {c}: the value of a data item.
 	// {d}: the percent of a data item(pie chart).
 	Formatter string
-	// Color specifies the color for the label.
-	Color Color
+	// FontStyle specifies the font and style for the label.
+	FontStyle FontStyle
 	// Show flag for label
 	Show bool
 	// Distance to the host graphic element.
@@ -63,8 +59,6 @@ type SeriesLabel struct {
 	Position string
 	// Offset specifies an offset from the position.
 	Offset Offset
-	// FontSize is the size for text rendering.
-	FontSize float64
 }
 
 const (
@@ -98,8 +92,6 @@ type Series struct {
 	Data []SeriesData
 	// YAxisIndex is the index for the axis, it must be 0 or 1.
 	YAxisIndex int
-	// Style represents the style for the series.
-	Style chartdraw.Style
 	// Label provides the series labels.
 	Label SeriesLabel
 	// Name specifies a name for the series.
diff --git a/series_label.go b/series_label.go
index 41f3e6b..9033414 100644
--- a/series_label.go
+++ b/series_label.go
@@ -7,11 +7,11 @@ import (
 )
 
 type labelRenderValue struct {
-	Text    string
-	Style   Style
-	X       int
-	Y       int
-	Radians float64
+	Text      string
+	FontStyle FontStyle
+	X         int
+	Y         int
+	Radians   float64
 }
 
 type LabelValue struct {
@@ -20,8 +20,7 @@ type LabelValue struct {
 	X         int
 	Y         int
 	Radians   float64
-	FontColor Color
-	FontSize  float64
+	FontStyle FontStyle
 	Orient    string
 	Offset    Offset
 }
@@ -61,33 +60,34 @@ func (o *SeriesLabelPainter) Add(value LabelValue) {
 		distance = 5
 	}
 	text := NewValueLabelFormatter(o.seriesNames, label.Formatter)(value.Index, value.Value, -1)
-	labelStyle := Style{
+	labelStyle := FontStyle{
 		FontColor: o.theme.GetTextColor(),
 		FontSize:  labelFontSize,
-		Font:      o.font,
-	}
-	if value.FontSize != 0 {
-		labelStyle.FontSize = value.FontSize
+		Font:      getPreferredFont(label.FontStyle.Font, value.FontStyle.Font, o.font),
 	}
-	if !value.FontColor.IsZero() {
-		label.Color = value.FontColor
+	if label.FontStyle.FontSize != 0 {
+		labelStyle.FontSize = label.FontStyle.FontSize
+	} else if value.FontStyle.FontSize != 0 {
+		labelStyle.FontSize = value.FontStyle.FontSize
 	}
-	if !label.Color.IsZero() {
-		labelStyle.FontColor = label.Color
+	if !label.FontStyle.FontColor.IsZero() {
+		labelStyle.FontColor = label.FontStyle.FontColor
+	} else if !value.FontStyle.FontColor.IsZero() {
+		labelStyle.FontColor = value.FontStyle.FontColor
 	}
 	p := o.p
-	p.OverrideDrawingStyle(labelStyle)
+	p.OverrideDrawingStyle(chartdraw.Style{FontStyle: labelStyle})
 	rotated := value.Radians != 0
 	if rotated {
 		p.SetTextRotation(value.Radians)
 	}
 	textBox := p.MeasureText(text)
 	renderValue := labelRenderValue{
-		Text:    text,
-		Style:   labelStyle,
-		X:       value.X,
-		Y:       value.Y,
-		Radians: value.Radians,
+		Text:      text,
+		FontStyle: labelStyle,
+		X:         value.X,
+		Y:         value.Y,
+		Radians:   value.Radians,
 	}
 	if value.Orient != OrientHorizontal {
 		renderValue.X -= textBox.Width() >> 1
@@ -110,7 +110,7 @@ func (o *SeriesLabelPainter) Add(value LabelValue) {
 
 func (o *SeriesLabelPainter) Render() (Box, error) {
 	for _, item := range o.values {
-		o.p.OverrideTextStyle(item.Style)
+		o.p.OverrideFontStyle(item.FontStyle)
 		if item.Radians != 0 {
 			o.p.TextRotation(item.Text, item.X, item.Y, item.Radians)
 		} else {
diff --git a/table.go b/table.go
index 92bbf6b..f2dd13d 100644
--- a/table.go
+++ b/table.go
@@ -3,8 +3,6 @@ package charts
 import (
 	"errors"
 
-	"github.com/golang/freetype/truetype"
-
 	"github.com/go-analyze/charts/chartdraw"
 	"github.com/go-analyze/charts/chartdraw/drawing"
 )
@@ -25,11 +23,13 @@ func NewTableChart(p *Painter, opt TableChartOption) *tableChart {
 type TableCell struct {
 	// Text the text of table cell
 	Text string
-	// Style the current style of table cell
-	Style Style
-	// Row the row index of table cell
+	// FontStyle contains the configuration for the cell text font.
+	FontStyle FontStyle
+	// FillColor sets a color for this table cell.
+	FillColor drawing.Color
+	// Row the row index of table cell.
 	Row int
-	// Column the column index of table cell
+	// Column the column index of table cell.
 	Column int
 }
 
@@ -50,12 +50,8 @@ type TableChartOption struct {
 	Spans []int
 	// TextAligns specifies the text alignment for each cell on the table.
 	TextAligns []string
-	// The font size of table contents.
-	FontSize float64
-	// Font is the font used to render the table.
-	Font *truetype.Font
-	// FontColor is the color used for text on the table.
-	FontColor Color
+	// FontStyle contains the configuration for the table text font.
+	FontStyle FontStyle
 	// HeaderBackgroundColor provides a background color of header row.
 	HeaderBackgroundColor Color
 	// HeaderFontColor specifies a text color for the header text.
@@ -64,68 +60,53 @@ type TableChartOption struct {
 	RowBackgroundColors []Color
 	// BackgroundColor specifies a general background color.
 	BackgroundColor Color
-	// CellTextStyle customize text style of table cell
-	CellTextStyle func(TableCell) *Style
-	// CellStyle customize drawing style of table cell
-	CellStyle func(TableCell) *Style
+	// CellModifier is an optional function to modify the style or content of a specific TableCell before they are rendered.
+	CellModifier func(TableCell) TableCell
 }
 
-type TableSetting struct {
-	// HeaderColor specifies the color of the header.
-	HeaderColor Color
-	// HeaderFontColor specifies the color of header text.
-	HeaderFontColor Color
-	// FontColor specifies the color of table text.
-	FontColor Color
-	// RowColors specifies an array of colors for each row.
-	RowColors []Color
-	// Padding specifies the padding of each cell.
-	CellPadding Box
+type tableSetting struct {
+	// headerColor specifies the color of the header.
+	headerColor Color
+	// headerFontColor specifies the color of header text.
+	headerFontColor Color
+	// fontColor specifies the color of table text.
+	fontColor Color
+	// rowColors specifies an array of colors for each row.
+	rowColors []Color
 }
 
-var TableLightThemeSetting = TableSetting{
-	HeaderColor:     Color{R: 220, G: 220, B: 220, A: 255},
-	HeaderFontColor: Color{R: 80, G: 80, B: 80, A: 255},
-	FontColor:       Color{R: 50, G: 50, B: 50, A: 255},
-	RowColors: []Color{
+var tableLightThemeSetting = tableSetting{
+	headerColor:     Color{R: 220, G: 220, B: 220, A: 255},
+	headerFontColor: Color{R: 80, G: 80, B: 80, A: 255},
+	fontColor:       Color{R: 50, G: 50, B: 50, A: 255},
+	rowColors: []Color{
 		drawing.ColorWhite,
 		{R: 245, G: 245, B: 245, A: 255},
 	},
-	CellPadding: Box{
-		Left:   10,
-		Top:    10,
-		Right:  10,
-		Bottom: 10,
-	},
 }
 
-var TableDarkThemeSetting = TableSetting{
-	HeaderColor:     Color{R: 38, G: 38, B: 42, A: 255},
-	HeaderFontColor: Color{R: 216, G: 217, B: 218, A: 255},
-	FontColor:       Color{R: 216, G: 217, B: 218, A: 255},
-	RowColors: []Color{
+var tableDarkThemeSetting = tableSetting{
+	headerColor:     Color{R: 38, G: 38, B: 42, A: 255},
+	headerFontColor: Color{R: 216, G: 217, B: 218, A: 255},
+	fontColor:       Color{R: 216, G: 217, B: 218, A: 255},
+	rowColors: []Color{
 		{R: 24, G: 24, B: 28, A: 255},
 		{R: 38, G: 38, B: 42, A: 255},
 	},
-	CellPadding: Box{
-		Left:   10,
-		Top:    10,
-		Right:  10,
-		Bottom: 10,
-	},
 }
 
 type renderInfo struct {
-	Width        int
-	Height       int
-	HeaderHeight int
-	RowHeights   []int
-	ColumnWidths []int
+	width        int
+	height       int
+	headerHeight int
+	rowHeights   []int
+	columnWidths []int
+	tableCells   [][]TableCell
 }
 
 func (t *tableChart) render() (*renderInfo, error) {
 	info := renderInfo{
-		RowHeights: make([]int, 0),
+		rowHeights: make([]int, 0),
 	}
 	p := t.p
 	if t.opt.Theme == nil {
@@ -135,24 +116,25 @@ func (t *tableChart) render() (*renderInfo, error) {
 	if len(opt.Header) == 0 {
 		return nil, errors.New("header can not be nil")
 	}
-	if opt.FontSize <= 0 {
-		opt.FontSize = 12
+	fontStyle := opt.FontStyle
+	if fontStyle.FontSize <= 0 {
+		fontStyle.FontSize = 12
 	}
-	if opt.FontColor.IsZero() {
+	if fontStyle.FontColor.IsZero() {
 		if opt.Theme.IsDark() {
-			opt.FontColor = TableDarkThemeSetting.FontColor
+			fontStyle.FontColor = tableDarkThemeSetting.fontColor
 		} else {
-			opt.FontColor = TableLightThemeSetting.FontColor
+			fontStyle.FontColor = tableLightThemeSetting.fontColor
 		}
 	}
-	if opt.Font == nil {
-		opt.Font = GetDefaultFont()
+	if fontStyle.Font == nil {
+		fontStyle.Font = GetDefaultFont()
 	}
 	if opt.HeaderFontColor.IsZero() {
 		if opt.Theme.IsDark() {
-			opt.HeaderFontColor = TableDarkThemeSetting.HeaderFontColor
+			opt.HeaderFontColor = tableDarkThemeSetting.headerFontColor
 		} else {
-			opt.HeaderFontColor = TableLightThemeSetting.HeaderFontColor
+			opt.HeaderFontColor = tableLightThemeSetting.headerFontColor
 		}
 	}
 
@@ -178,30 +160,18 @@ func (t *tableChart) render() (*renderInfo, error) {
 		}
 		columnWidths = append(columnWidths, values[index+1]-v)
 	}
-	info.ColumnWidths = columnWidths
+	info.columnWidths = columnWidths
 
 	height := 0
-	textStyle := Style{
-		FontSize:  opt.FontSize,
-		FontColor: opt.HeaderFontColor,
+	style := chartdraw.Style{
+		FontStyle: FontStyle{
+			FontSize:  fontStyle.FontSize,
+			FontColor: opt.HeaderFontColor,
+			Font:      fontStyle.Font,
+		},
 		FillColor: opt.HeaderFontColor,
-		Font:      opt.Font,
 	}
 
-	headerHeight := 0
-	if opt.Padding.IsZero() {
-		if opt.Theme.IsDark() {
-			opt.Padding = TableDarkThemeSetting.CellPadding
-		} else {
-			opt.Padding = TableLightThemeSetting.CellPadding
-		}
-	}
-	getCellTextStyle := opt.CellTextStyle
-	if getCellTextStyle == nil {
-		getCellTextStyle = func(_ TableCell) *Style {
-			return nil
-		}
-	}
 	// textAligns := opt.TextAligns
 	getTextAlign := func(index int) string {
 		if len(opt.TextAligns) <= index {
@@ -212,56 +182,66 @@ func (t *tableChart) render() (*renderInfo, error) {
 
 	// processing of the table cells
 	renderTableCells := func(
-		currentStyle Style,
+		style chartdraw.Style,
 		rowIndex int,
 		textList []string,
 		currentHeight int,
 		cellPadding Box,
-	) int {
+	) ([]TableCell, int) {
 		cellMaxHeight := 0
 		paddingHeight := cellPadding.Top + cellPadding.Bottom
 		paddingWidth := cellPadding.Left + cellPadding.Right
+		cells := make([]TableCell, len(textList))
 		for index, text := range textList {
-			cellStyle := getCellTextStyle(TableCell{
-				Text:   text,
-				Row:    rowIndex,
-				Column: index,
-				Style:  currentStyle,
-			})
-			if cellStyle == nil {
-				cellStyle = &currentStyle
+			tc := TableCell{
+				Text:      text,
+				Row:       rowIndex,
+				Column:    index,
+				FontStyle: style.FontStyle,
+				FillColor: style.FillColor,
+			}
+			if opt.CellModifier != nil {
+				tc = opt.CellModifier(tc)
+				// Update style values to capture any changes
+				style.FontStyle = tc.FontStyle
+				style.FillColor = tc.FillColor
 			}
-			p.SetStyle(*cellStyle)
+			cells[index] = tc
+			p.SetStyle(style)
 			x := values[index]
 			y := currentHeight + cellPadding.Top
 			width := values[index+1] - x
 			x += cellPadding.Left
 			width -= paddingWidth
-			box := p.TextFit(text, x, y+int(opt.FontSize), width, getTextAlign(index))
+			box := p.TextFit(text, x, y+int(fontStyle.FontSize), width, getTextAlign(index))
 			// calculate the highest height
 			if box.Height()+paddingHeight > cellMaxHeight {
 				cellMaxHeight = box.Height() + paddingHeight
 			}
 		}
-		return cellMaxHeight
+		return cells, cellMaxHeight
 	}
 
+	info.tableCells = make([][]TableCell, len(opt.Data)+1)
+
 	// processing of the table headers
-	headerHeight = renderTableCells(textStyle, 0, opt.Header, height, opt.Padding)
+	headerCells, headerHeight := renderTableCells(style, 0, opt.Header, height, opt.Padding)
+	info.tableCells[0] = headerCells
 	height += headerHeight
-	info.HeaderHeight = headerHeight
+	info.headerHeight = headerHeight
 
 	// processing of the table contents
-	textStyle.FontColor = opt.FontColor
-	textStyle.FillColor = opt.FontColor
+	style.FontColor = fontStyle.FontColor
+	style.FillColor = fontStyle.FontColor
 	for index, textList := range opt.Data {
-		cellHeight := renderTableCells(textStyle, index+1, textList, height, opt.Padding)
-		info.RowHeights = append(info.RowHeights, cellHeight)
+		newCells, cellHeight := renderTableCells(style, index+1, textList, height, opt.Padding)
+		info.tableCells[index+1] = newCells
+		info.rowHeights = append(info.rowHeights, cellHeight)
 		height += cellHeight
 	}
 
-	info.Width = p.Width()
-	info.Height = height
+	info.width = p.Width()
+	info.height = height
 	return &info, nil
 }
 
@@ -274,25 +254,26 @@ func (t *tableChart) renderWithInfo(info *renderInfo) (Box, error) {
 	if !opt.BackgroundColor.IsZero() {
 		p.SetBackground(p.Width(), p.Height(), opt.BackgroundColor)
 	}
+
 	if opt.HeaderBackgroundColor.IsZero() {
 		if opt.Theme.IsDark() {
-			opt.HeaderBackgroundColor = TableDarkThemeSetting.HeaderColor
+			opt.HeaderBackgroundColor = tableDarkThemeSetting.headerColor
 		} else {
-			opt.HeaderBackgroundColor = TableLightThemeSetting.HeaderColor
+			opt.HeaderBackgroundColor = tableLightThemeSetting.headerColor
 		}
 	}
+	p.SetBackground(info.width, info.headerHeight, opt.HeaderBackgroundColor, true)
 
-	// if the header background colors is set
-	p.SetBackground(info.Width, info.HeaderHeight, opt.HeaderBackgroundColor, true)
-	currentHeight := info.HeaderHeight
 	if opt.RowBackgroundColors == nil {
 		if opt.Theme.IsDark() {
-			opt.RowBackgroundColors = TableDarkThemeSetting.RowColors
+			opt.RowBackgroundColors = tableDarkThemeSetting.rowColors
 		} else {
-			opt.RowBackgroundColors = TableLightThemeSetting.RowColors
+			opt.RowBackgroundColors = tableLightThemeSetting.rowColors
 		}
 	}
-	for index, h := range info.RowHeights {
+
+	currentHeight := info.headerHeight
+	for index, h := range info.rowHeights {
 		color := opt.RowBackgroundColors[index%len(opt.RowBackgroundColors)]
 		child := p.Child(PainterPaddingOption(Box{
 			Top: currentHeight,
@@ -301,35 +282,29 @@ func (t *tableChart) renderWithInfo(info *renderInfo) (Box, error) {
 		currentHeight += h
 	}
 	// adjust the background color according to the set table style
-	getCellStyle := opt.CellStyle
-	if getCellStyle != nil {
+	if opt.CellModifier != nil {
 		arr := [][]string{
 			opt.Header,
 		}
 		arr = append(arr, opt.Data...)
 		top := 0
-		heights := []int{info.HeaderHeight}
-		heights = append(heights, info.RowHeights...)
+		heights := []int{info.headerHeight}
+		heights = append(heights, info.rowHeights...)
 		// loop through all table cells to generate background color
-		for i, textList := range arr {
+		for i, _ := range arr {
 			left := 0
-			for j, v := range textList {
-				style := getCellStyle(TableCell{
-					Text:   v,
-					Row:    i,
-					Column: j,
-				})
-				if style != nil && !style.FillColor.IsZero() {
-					padding := style.Padding
+			for j, tc := range info.tableCells[i] {
+				if !tc.FillColor.IsZero() {
+					padding := opt.Padding
 					child := p.Child(PainterPaddingOption(Box{
 						Top:  top + padding.Top,
 						Left: left + padding.Left,
 					}))
-					w := info.ColumnWidths[j] - padding.Left - padding.Top
+					w := info.columnWidths[j] - padding.Left - padding.Top
 					h := heights[i] - padding.Top - padding.Bottom
-					child.SetBackground(w, h, style.FillColor, true)
+					child.SetBackground(w, h, tc.FillColor, true)
 				}
-				left += info.ColumnWidths[j]
+				left += info.columnWidths[j]
 			}
 			top += heights[i]
 		}
@@ -340,8 +315,8 @@ func (t *tableChart) renderWithInfo(info *renderInfo) (Box, error) {
 	}
 
 	return Box{
-		Right:  info.Width,
-		Bottom: info.Height,
+		Right:  info.width,
+		Bottom: info.height,
 	}, nil
 }
 
diff --git a/theme.go b/theme.go
index c006c1d..08549df 100644
--- a/theme.go
+++ b/theme.go
@@ -41,6 +41,10 @@ type ColorPalette interface {
 	// WithTextColor will provide a new ColorPalette that uses the specified color for text.
 	// This is generally recommended over using the FontColor config values.
 	WithTextColor(Color) ColorPalette
+	// WithSeriesColors will provide a new ColorPalette that uses the specified series colors.
+	WithSeriesColors([]Color) ColorPalette
+	// WithBackgroundColor will provide a new ColorPalette that uses the specified color for the background.
+	WithBackgroundColor(Color) ColorPalette
 }
 
 type themeColorPalette struct {
@@ -343,3 +347,30 @@ func (t *themeColorPalette) WithTextColor(c Color) ColorPalette {
 	copy.textColor = c
 	return &copy
 }
+
+func (t *themeColorPalette) WithSeriesColors(colors []Color) ColorPalette {
+	copy := *t
+	if len(colors) == 0 { // ignore invalid input rather than panic later
+		copy.name += "-ignored_invalid_series_mod"
+		return &copy
+	}
+	copy.name += "-series_mod"
+	copy.seriesColors = colors
+	return &copy
+}
+
+func (t *themeColorPalette) WithBackgroundColor(color Color) ColorPalette {
+	copy := *t
+	copy.name += "-background_mod"
+	copy.backgroundColor = color
+	updatedDark := !isLightColor(color)
+	if copy.isDarkMode != updatedDark {
+		copy.isDarkMode = updatedDark
+		if copy.isDarkMode {
+			copy.name += "_dark"
+		} else {
+			copy.name += "_light"
+		}
+	}
+	return &copy
+}
diff --git a/title.go b/title.go
index 8573453..6c449f3 100644
--- a/title.go
+++ b/title.go
@@ -2,8 +2,6 @@ package charts
 
 import (
 	"strings"
-
-	"github.com/golang/freetype/truetype"
 )
 
 type TitleOption struct {
@@ -21,23 +19,17 @@ type TitleOption struct {
 	// Top is the distance between title component and the top side of the container.
 	// It can be pixel value (20) or percentage value (20%).
 	Top string
-	// The font size of title.
-	FontSize float64
-	// Font is the font used to render the title.
-	Font *truetype.Font
-	// FontColor is the color used for text on the title.
-	FontColor Color
-	// SubtextFontSize specifies the size of the subtext.
-	SubtextFontSize float64
-	// SubtextFontColor specifies a unique color for the subtext.
-	SubtextFontColor Color
+	// FontStyle specifies the font, size, and style for rendering the title.
+	FontStyle FontStyle
+	// SubtextFontStyle specifies the font, size, and style for rendering the subtext.
+	SubtextFontStyle FontStyle
 }
 
 type titleMeasureOption struct {
 	width  int
 	height int
 	text   string
-	style  Style
+	style  FontStyle
 }
 
 func splitTitleText(text string) []string {
@@ -83,50 +75,45 @@ func (t *titlePainter) Render() (Box, error) {
 
 	measureOptions := make([]titleMeasureOption, 0)
 
-	if opt.Font == nil {
-		opt.Font = GetDefaultFont()
+	fontStyle := opt.FontStyle
+	if fontStyle.Font == nil {
+		fontStyle.Font = GetDefaultFont()
 	}
-	if opt.FontColor.IsZero() {
-		opt.FontColor = theme.GetTextColor()
+	if fontStyle.FontColor.IsZero() {
+		fontStyle.FontColor = theme.GetTextColor()
 	}
-	if opt.FontSize == 0 {
-		opt.FontSize = defaultFontSize
+	if fontStyle.FontSize == 0 {
+		fontStyle.FontSize = defaultFontSize
 	}
-	if opt.SubtextFontColor.IsZero() {
-		opt.SubtextFontColor = opt.FontColor
+	subtextFontStyle := opt.SubtextFontStyle
+	if subtextFontStyle.Font == nil {
+		subtextFontStyle.Font = fontStyle.Font
 	}
-	if opt.SubtextFontSize == 0 {
-		opt.SubtextFontSize = opt.FontSize
+	if subtextFontStyle.FontColor.IsZero() {
+		subtextFontStyle.FontColor = fontStyle.FontColor
 	}
-
-	titleTextStyle := Style{
-		Font:      opt.Font,
-		FontSize:  opt.FontSize,
-		FontColor: opt.FontColor,
+	if subtextFontStyle.FontSize == 0 {
+		subtextFontStyle.FontSize = fontStyle.FontSize
 	}
+
 	// main title
 	for _, v := range splitTitleText(opt.Text) {
 		measureOptions = append(measureOptions, titleMeasureOption{
 			text:  v,
-			style: titleTextStyle,
+			style: fontStyle,
 		})
 	}
-	subtextStyle := Style{
-		Font:      opt.Font,
-		FontSize:  opt.SubtextFontSize,
-		FontColor: opt.SubtextFontColor,
-	}
 	// subtitle
 	for _, v := range splitTitleText(opt.Subtext) {
 		measureOptions = append(measureOptions, titleMeasureOption{
 			text:  v,
-			style: subtextStyle,
+			style: subtextFontStyle,
 		})
 	}
 	textMaxWidth := 0
 	textMaxHeight := 0
 	for index, item := range measureOptions {
-		p.OverrideTextStyle(item.style)
+		p.OverrideFontStyle(item.style)
 		textBox := p.MeasureText(item.text)
 
 		w := textBox.Width()
@@ -166,7 +153,7 @@ func (t *titlePainter) Render() (Box, error) {
 		}
 	}
 	for _, item := range measureOptions {
-		p.OverrideTextStyle(item.style)
+		p.OverrideFontStyle(item.style)
 		x := titleX + (textMaxWidth-item.width)>>1
 		y := titleY + item.height
 		p.Text(item.text, x, y)
diff --git a/xaxis.go b/xaxis.go
index 3690662..504d8cc 100644
--- a/xaxis.go
+++ b/xaxis.go
@@ -1,9 +1,5 @@
 package charts
 
-import (
-	"github.com/golang/freetype/truetype"
-)
-
 type XAxisOption struct {
 	// Show specifies if the x-axis should be rendered, set this to *false (through False()) to hide the axis.
 	Show *bool
@@ -18,12 +14,8 @@ type XAxisOption struct {
 	// BoundaryGap specifies that the chart should have additional space on the left and right, with data points being
 	// centered between two axis ticks.  Enabled by default, specify *false (through False()) to change the spacing.
 	BoundaryGap *bool
-	// FontSize specifies the font size of each label.
-	FontSize float64
-	// Font is the font used to render each label.
-	Font *truetype.Font
-	// FontColor is the color used for text rendered.
-	FontColor Color
+	// FontStyle specifies the font configuration for each label.
+	FontStyle FontStyle
 	// TextRotation are the radians for rotating the label.
 	TextRotation float64
 	// LabelOffset is the offset of each label.
@@ -62,9 +54,7 @@ func (opt *XAxisOption) ToAxisOption() AxisOption {
 		DataStartIndex:       opt.DataStartIndex,
 		BoundaryGap:          opt.BoundaryGap,
 		Position:             position,
-		FontSize:             opt.FontSize,
-		Font:                 opt.Font,
-		FontColor:            opt.FontColor,
+		FontStyle:            opt.FontStyle,
 		Show:                 opt.Show,
 		Unit:                 opt.Unit,
 		LabelCount:           opt.LabelCount,
diff --git a/yaxis.go b/yaxis.go
index f49bcc9..3d25e7c 100644
--- a/yaxis.go
+++ b/yaxis.go
@@ -1,9 +1,5 @@
 package charts
 
-import (
-	"github.com/golang/freetype/truetype"
-)
-
 type YAxisOption struct {
 	// Show specifies if the y-axis should be rendered, set this to *false (through False()) to hide the axis.
 	Show *bool
@@ -21,12 +17,8 @@ type YAxisOption struct {
 	Data []string
 	// Position describes the position of y-axis, it can be 'left' or 'right'.
 	Position string
-	// FontSize specifies the font size of each label.
-	FontSize float64
-	// Font is the font used to render each label.
-	Font *truetype.Font
-	// FontColor is the color used for text rendered.
-	FontColor Color
+	// FontStyle specifies the font configuration for each label.
+	FontStyle FontStyle
 	// Formatter for replacing y-axis text values.
 	Formatter string
 	// Unit is a suggestion for how large the axis step is, this is a recommendation only. Larger numbers result in fewer labels.
@@ -72,10 +64,8 @@ func (opt *YAxisOption) ToAxisOption(p *Painter) AxisOption {
 		Theme:                theme,
 		Data:                 opt.Data,
 		Position:             position,
-		FontSize:             opt.FontSize,
+		FontStyle:            opt.FontStyle,
 		StrokeWidth:          -1,
-		Font:                 opt.Font,
-		FontColor:            opt.FontColor,
 		BoundaryGap:          False(),
 		Unit:                 opt.Unit,
 		LabelCount:           opt.LabelCount,
@@ -85,7 +75,9 @@ func (opt *YAxisOption) ToAxisOption(p *Painter) AxisOption {
 		Show:                 opt.Show,
 	}
 	if !opt.AxisColor.IsZero() {
-		axisOpt.FontColor = opt.AxisColor
+		if axisOpt.FontStyle.FontColor.IsZero() {
+			axisOpt.FontStyle.FontColor = opt.AxisColor
+		}
 		axisOpt.Theme = theme.WithAxisColor(opt.AxisColor)
 	}
 	if opt.isCategoryAxis {


Test patch:
diff --git a/chartdraw/annotation_series_test.go b/chartdraw/annotation_series_test.go
index 947b4b1..77ca5e9 100644
--- a/chartdraw/annotation_series_test.go
+++ b/chartdraw/annotation_series_test.go
@@ -44,8 +44,10 @@ func TestAnnotationSeriesMeasure(t *testing.T) {
 		Bottom: 105,
 	}
 	sd := Style{
-		FontSize: 10.0,
-		Font:     f,
+		FontStyle: FontStyle{
+			FontSize: 10.0,
+			Font:     f,
+		},
 	}
 
 	box := as.Measure(r, cb, xrange, yrange, sd)
@@ -94,8 +96,10 @@ func TestAnnotationSeriesRender(t *testing.T) {
 		Bottom: 105,
 	}
 	sd := Style{
-		FontSize: 10.0,
-		Font:     f,
+		FontStyle: FontStyle{
+			FontSize: 10.0,
+			Font:     f,
+		},
 	}
 
 	as.Render(r, cb, xrange, yrange, sd)
diff --git a/chartdraw/style_test.go b/chartdraw/style_test.go
index 85686b5..f84748c 100644
--- a/chartdraw/style_test.go
+++ b/chartdraw/style_test.go
@@ -23,13 +23,13 @@ func TestStyleIsZero(t *testing.T) {
 	strokeWidth := Style{StrokeWidth: 5.0}
 	assert.False(t, strokeWidth.IsZero())
 
-	fontSize := Style{FontSize: 12.0}
+	fontSize := Style{FontStyle: FontStyle{FontSize: 12.0}}
 	assert.False(t, fontSize.IsZero())
 
-	fontColor := Style{FontColor: drawing.ColorWhite}
+	fontColor := Style{FontStyle: FontStyle{FontColor: drawing.ColorWhite}}
 	assert.False(t, fontColor.IsZero())
 
-	font := Style{Font: &truetype.Font{}}
+	font := Style{FontStyle: FontStyle{Font: &truetype.Font{}}}
 	assert.False(t, font.IsZero())
 }
 
@@ -68,7 +68,7 @@ func TestStyleGetFontSize(t *testing.T) {
 	assert.Equal(t, DefaultFontSize, unset.GetFontSize())
 	assert.Equal(t, DefaultFontSize+1, unset.GetFontSize(DefaultFontSize+1))
 
-	set := Style{FontSize: DefaultFontSize + 2}
+	set := Style{FontStyle: FontStyle{FontSize: DefaultFontSize + 2}}
 	assert.Equal(t, DefaultFontSize+2, set.GetFontSize())
 	assert.Equal(t, DefaultFontSize+2, set.GetFontSize(DefaultFontSize+1))
 }
@@ -78,7 +78,7 @@ func TestStyleGetFontColor(t *testing.T) {
 	assert.Equal(t, drawing.ColorTransparent, unset.GetFontColor())
 	assert.Equal(t, drawing.ColorWhite, unset.GetFontColor(drawing.ColorWhite))
 
-	set := Style{FontColor: drawing.ColorWhite}
+	set := Style{FontStyle: FontStyle{FontColor: drawing.ColorWhite}}
 	assert.Equal(t, drawing.ColorWhite, set.GetFontColor())
 	assert.Equal(t, drawing.ColorWhite, set.GetFontColor(drawing.ColorBlack))
 }
@@ -91,7 +91,7 @@ func TestStyleGetFont(t *testing.T) {
 	require.Nil(t, unset.GetFont())
 	assert.Equal(t, f, unset.GetFont(f))
 
-	set := Style{Font: f}
+	set := Style{FontStyle: FontStyle{Font: f}}
 	require.NotNil(t, set.GetFont())
 }
 
@@ -121,9 +121,11 @@ func TestStyleWithDefaultsFrom(t *testing.T) {
 		StrokeColor: drawing.ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   drawing.ColorWhite,
-		FontColor:   drawing.ColorWhite,
-		Font:        f,
-		Padding:     DefaultBackgroundPadding,
+		FontStyle: FontStyle{
+			FontColor: drawing.ColorWhite,
+			Font:      f,
+		},
+		Padding: DefaultBackgroundPadding,
 	}
 
 	coalesced := unset.InheritFrom(set)
@@ -135,8 +137,10 @@ func TestStyleGetStrokeOptions(t *testing.T) {
 		StrokeColor: drawing.ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   drawing.ColorWhite,
-		FontColor:   drawing.ColorWhite,
-		Padding:     DefaultBackgroundPadding,
+		FontStyle: FontStyle{
+			FontColor: drawing.ColorWhite,
+		},
+		Padding: DefaultBackgroundPadding,
 	}
 	svgStroke := set.GetStrokeOptions()
 	assert.False(t, svgStroke.StrokeColor.IsZero())
@@ -150,8 +154,10 @@ func TestStyleGetFillOptions(t *testing.T) {
 		StrokeColor: drawing.ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   drawing.ColorWhite,
-		FontColor:   drawing.ColorWhite,
-		Padding:     DefaultBackgroundPadding,
+		FontStyle: FontStyle{
+			FontColor: drawing.ColorWhite,
+		},
+		Padding: DefaultBackgroundPadding,
 	}
 	svgFill := set.GetFillOptions()
 	assert.False(t, svgFill.FillColor.IsZero())
@@ -165,8 +171,10 @@ func TestStyleGetFillAndStrokeOptions(t *testing.T) {
 		StrokeColor: drawing.ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   drawing.ColorWhite,
-		FontColor:   drawing.ColorWhite,
-		Padding:     DefaultBackgroundPadding,
+		FontStyle: FontStyle{
+			FontColor: drawing.ColorWhite,
+		},
+		Padding: DefaultBackgroundPadding,
 	}
 	svgFillAndStroke := set.GetFillAndStrokeOptions()
 	assert.False(t, svgFillAndStroke.FillColor.IsZero())
@@ -180,8 +188,10 @@ func TestStyleGetTextOptions(t *testing.T) {
 		StrokeColor: drawing.ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   drawing.ColorWhite,
-		FontColor:   drawing.ColorWhite,
-		Padding:     DefaultBackgroundPadding,
+		FontStyle: FontStyle{
+			FontColor: drawing.ColorWhite,
+		},
+		Padding: DefaultBackgroundPadding,
 	}
 	svgStroke := set.GetTextOptions()
 	assert.True(t, svgStroke.StrokeColor.IsZero())
diff --git a/chartdraw/text_test.go b/chartdraw/text_test.go
index 419af49..95df418 100644
--- a/chartdraw/text_test.go
+++ b/chartdraw/text_test.go
@@ -13,7 +13,7 @@ func TestTextWrapWord(t *testing.T) {
 	f, err := GetDefaultFont()
 	require.NoError(t, err)
 
-	basicTextStyle := Style{Font: f, FontSize: 24}
+	basicTextStyle := Style{FontStyle: FontStyle{Font: f, FontSize: 24}}
 
 	output := Text.WrapFitWord(r, "this is a test string", 100, basicTextStyle)
 	assert.NotEmpty(t, output)
@@ -47,7 +47,7 @@ func TestTextWrapRune(t *testing.T) {
 	f, err := GetDefaultFont()
 	require.NoError(t, err)
 
-	basicTextStyle := Style{Font: f, FontSize: 24}
+	basicTextStyle := Style{FontStyle: FontStyle{Font: f, FontSize: 24}}
 
 	output := Text.WrapFitRune(r, "this is a test string", 150, basicTextStyle)
 	assert.NotEmpty(t, output)
diff --git a/chartdraw/vector_renderer_test.go b/chartdraw/vector_renderer_test.go
index 9b9c4cd..05586d0 100644
--- a/chartdraw/vector_renderer_test.go
+++ b/chartdraw/vector_renderer_test.go
@@ -57,9 +57,11 @@ func TestCanvasStyleSVG(t *testing.T) {
 		StrokeColor: drawing.ColorWhite,
 		StrokeWidth: 5.0,
 		FillColor:   drawing.ColorWhite,
-		FontColor:   drawing.ColorWhite,
-		Font:        f,
-		Padding:     DefaultBackgroundPadding,
+		FontStyle: FontStyle{
+			FontColor: drawing.ColorWhite,
+			Font:      f,
+		},
+		Padding: DefaultBackgroundPadding,
 	}
 
 	canvas := &canvas{dpi: DefaultDPI}
diff --git a/chartdraw/xaxis_test.go b/chartdraw/xaxis_test.go
index 2d0c490..7147390 100644
--- a/chartdraw/xaxis_test.go
+++ b/chartdraw/xaxis_test.go
@@ -17,8 +17,10 @@ func TestXAxisGetTicks(t *testing.T) {
 	xa := XAxis{}
 	xr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
 	styleDefaults := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	vf := FloatValueFormatter
 	ticks := xa.GetTicks(r, xr, styleDefaults, vf)
@@ -37,8 +39,10 @@ func TestXAxisGetTicksWithUserDefaults(t *testing.T) {
 	}
 	xr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
 	styleDefaults := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	vf := FloatValueFormatter
 	ticks := xa.GetTicks(r, xr, styleDefaults, vf)
@@ -49,8 +53,10 @@ func TestXAxisMeasure(t *testing.T) {
 	f, err := GetDefaultFont()
 	require.NoError(t, err)
 	style := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	r, err := PNG(100, 100)
 	require.NoError(t, err)
diff --git a/chartdraw/yaxis_test.go b/chartdraw/yaxis_test.go
index 5259bac..b77ad93 100644
--- a/chartdraw/yaxis_test.go
+++ b/chartdraw/yaxis_test.go
@@ -17,8 +17,10 @@ func TestYAxisGetTicks(t *testing.T) {
 	ya := YAxis{}
 	yr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
 	styleDefaults := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	vf := FloatValueFormatter
 	ticks := ya.GetTicks(r, yr, styleDefaults, vf)
@@ -37,8 +39,10 @@ func TestYAxisGetTicksWithUserDefaults(t *testing.T) {
 	}
 	yr := &ContinuousRange{Min: 10, Max: 100, Domain: 1024}
 	styleDefaults := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	vf := FloatValueFormatter
 	ticks := ya.GetTicks(r, yr, styleDefaults, vf)
@@ -49,8 +53,10 @@ func TestYAxisMeasure(t *testing.T) {
 	f, err := GetDefaultFont()
 	require.NoError(t, err)
 	style := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	r, err := PNG(100, 100)
 	require.NoError(t, err)
@@ -65,8 +71,10 @@ func TestYAxisSecondaryMeasure(t *testing.T) {
 	f, err := GetDefaultFont()
 	require.NoError(t, err)
 	style := Style{
-		Font:     f,
-		FontSize: 10.0,
+		FontStyle: FontStyle{
+			Font:     f,
+			FontSize: 10.0,
+		},
 	}
 	r, err := PNG(100, 100)
 	require.NoError(t, err)
diff --git a/echarts_test.go b/echarts_test.go
index d1a517f..992c366 100644
--- a/echarts_test.go
+++ b/echarts_test.go
@@ -7,8 +7,6 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-
-	"github.com/go-analyze/charts/chartdraw/drawing"
 )
 
 func TestConvertToArray(t *testing.T) {
@@ -68,18 +66,6 @@ func TestEChartsXAxis(t *testing.T) {
 	}, ex)
 }
 
-func TestEChartStyle(t *testing.T) {
-	es := EChartStyle{
-		Color: "#999",
-	}
-	color := drawing.Color{R: 153, G: 153, B: 153, A: 255}
-	assert.Equal(t, Style{
-		FillColor:   color,
-		FontColor:   color,
-		StrokeColor: color,
-	}, es.ToStyle())
-}
-
 func TestEChartsPadding(t *testing.T) {
 	eb := EChartsPadding{}
 
diff --git a/font_test.go b/font_test.go
index 5d1ae0d..c089503 100644
--- a/font_test.go
+++ b/font_test.go
@@ -35,14 +35,16 @@ func TestCustomFontSizeRender(t *testing.T) {
 	require.NoError(t, err)
 
 	opt := makeBasicLineChartOption()
-	opt.XAxis.FontSize = 4.0
+	opt.XAxis.FontStyle.FontSize = 4.0
 	opt.YAxis = []YAxisOption{
 		{
-			FontSize: 4.0,
+			FontStyle: FontStyle{
+				FontSize: 4.0,
+			},
 		},
 	}
-	opt.Title.FontSize = 4.0
-	opt.Legend.FontSize = 4.0
+	opt.Title.FontStyle.FontSize = 4.0
+	opt.Legend.FontStyle.FontSize = 4.0
 
 	_, err = NewLineChart(p, opt).Render()
 	require.NoError(t, err)
diff --git a/painter_test.go b/painter_test.go
index 585c0b2..77eb2a2 100644
--- a/painter_test.go
+++ b/painter_test.go
@@ -25,7 +25,7 @@ func TestPainterOption(t *testing.T) {
 		PainterBoxOption(Box{Right: 400, Bottom: 300}),
 		PainterPaddingOption(Box{Left: 1, Top: 2, Right: 3, Bottom: 4}),
 		PainterFontOption(font),
-		PainterStyleOption(Style{ClassName: "test"}),
+		PainterStyleOption(chartdraw.Style{ClassName: "test"}),
 	)
 	require.NoError(t, err)
 	assert.Equal(t, Box{
@@ -72,7 +72,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "line",
 			fn: func(p *Painter) {
-				p.SetDrawingStyle(Style{
+				p.SetDrawingStyle(chartdraw.Style{
 					StrokeColor: drawing.ColorBlack,
 					StrokeWidth: 1,
 				})
@@ -93,7 +93,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "arc",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: drawing.ColorBlack,
 					FillColor:   drawing.ColorBlue,
@@ -107,7 +107,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "pin",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: Color{R: 84, G: 112, B: 198, A: 255},
 					FillColor:   Color{R: 84, G: 112, B: 198, A: 255},
@@ -119,7 +119,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "arrow_left",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: Color{R: 84, G: 112, B: 198, A: 255},
 					FillColor:   Color{R: 84, G: 112, B: 198, A: 255},
@@ -131,7 +131,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "arrow_right",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: Color{R: 84, G: 112, B: 198, A: 255},
 					FillColor:   Color{R: 84, G: 112, B: 198, A: 255},
@@ -143,7 +143,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "arrow_top",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: Color{R: 84, G: 112, B: 198, A: 255},
 					FillColor:   Color{R: 84, G: 112, B: 198, A: 255},
@@ -155,7 +155,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "arrow_bottom",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: Color{R: 84, G: 112, B: 198, A: 255},
 					FillColor:   Color{R: 84, G: 112, B: 198, A: 255},
@@ -167,7 +167,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "mark_line",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth:     1,
 					StrokeColor:     Color{R: 84, G: 112, B: 198, A: 255},
 					FillColor:       Color{R: 84, G: 112, B: 198, A: 255},
@@ -180,7 +180,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "polygon",
 			fn: func(p *Painter) {
-				p.SetStyle(Style{
+				p.SetStyle(chartdraw.Style{
 					StrokeWidth: 1,
 					StrokeColor: Color{R: 84, G: 112, B: 198, A: 255},
 				})
@@ -191,7 +191,7 @@ func TestPainter(t *testing.T) {
 		{
 			name: "fillArea",
 			fn: func(p *Painter) {
-				p.SetDrawingStyle(Style{
+				p.SetDrawingStyle(chartdraw.Style{
 					FillColor: Color{R: 84, G: 112, B: 198, A: 255},
 				})
 				p.FillArea([]Point{
@@ -274,7 +274,7 @@ func TestRoundedRect(t *testing.T) {
 				OutputFormat: ChartOutputSVG,
 			})
 			require.NoError(t, err)
-			p.OverrideDrawingStyle(Style{
+			p.OverrideDrawingStyle(chartdraw.Style{
 				FillColor:   drawing.ColorBlue,
 				StrokeWidth: 1,
 				StrokeColor: drawing.ColorBlue,
@@ -296,12 +296,12 @@ func TestPainterTextFit(t *testing.T) {
 		Height:       300,
 	})
 	require.NoError(t, err)
-	style := Style{
+	style := FontStyle{
 		FontSize:  12,
 		FontColor: chartdraw.ColorBlack,
 		Font:      GetDefaultFont(),
 	}
-	p.SetStyle(style)
+	p.SetStyle(chartdraw.Style{FontStyle: style})
 	box := p.TextFit("Hello World!", 0, 20, 80)
 	assert.Equal(t, chartdraw.Box{Right: 45, Bottom: 35}, box)
 
diff --git a/table_test.go b/table_test.go
index 3c886f5..6f1d214 100644
--- a/table_test.go
+++ b/table_test.go
@@ -5,99 +5,103 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/require"
+
+	"github.com/go-analyze/charts/chartdraw/drawing"
 )
 
+func makeDefaultTableChartOptions() TableChartOption {
+	return TableChartOption{
+		Padding: Box{
+			Left:   10,
+			Top:    10,
+			Right:  10,
+			Bottom: 10,
+		},
+		Header: []string{
+			"Name",
+			"Age",
+			"Address",
+			"Tag",
+			"Action",
+		},
+		Data: [][]string{
+			{
+				"John Brown",
+				"32",
+				"New York No. 1 Lake Park",
+				"nice, developer",
+				"Send Mail",
+			},
+			{
+				"Jim Green	",
+				"42",
+				"London No. 1 Lake Park",
+				"wow",
+				"Send Mail",
+			},
+			{
+				"Joe Black	",
+				"32",
+				"Sidney No. 1 Lake Park",
+				"cool, teacher",
+				"Send Mail",
+			},
+		},
+	}
+}
+
 func TestTableChart(t *testing.T) {
 	t.Parallel()
 
 	tests := []struct {
+		name        string
 		theme       ColorPalette
 		makeOptions func() TableChartOption
 		result      string
 	}{
 		{
+			name:  "default_theme_spans",
 			theme: nil, // use default
 			makeOptions: func() TableChartOption {
-				return TableChartOption{
-					Header: []string{
-						"Name",
-						"Age",
-						"Address",
-						"Tag",
-						"Action",
-					},
-					Spans: []int{
-						1,
-						1,
-						2,
-						1,
-						// span and header do not match, and are automatically set to 1 at the end
-						// 1,
-					},
-					Data: [][]string{
-						{
-							"John Brown",
-							"32",
-							"New York No. 1 Lake Park",
-							"nice, developer",
-							"Send Mail",
-						},
-						{
-							"Jim Green	",
-							"42",
-							"London No. 1 Lake Park",
-							"wow",
-							"Send Mail",
-						},
-						{
-							"Joe Black	",
-							"32",
-							"Sidney No. 1 Lake Park",
-							"cool, teacher",
-							"Send Mail",
-						},
-					},
+				opt := makeDefaultTableChartOptions()
+				opt.Spans = []int{
+					1,
+					1,
+					2,
+					1,
+					// span and header do not match, and are automatically set to 1 at the end
+					// 1,
 				}
+				return opt
 			},
 			result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 600 400\"><path  d=\"M 0 0\nL 600 0\nL 600 35\nL 0 35\nL 0 0\" style=\"stroke-width:0;stroke:none;fill:rgba(220,220,220,1.0)\"/><path  d=\"M 0 35\nL 600 35\nL 600 90\nL 0 90\nL 0 35\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 0 90\nL 600 90\nL 600 125\nL 0 125\nL 0 90\" style=\"stroke-width:0;stroke:none;fill:rgba(245,245,245,1.0)\"/><path  d=\"M 0 125\nL 600 125\nL 600 180\nL 0 180\nL 0 125\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><text x=\"10\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(80,80,80,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Name</text><text x=\"110\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(80,80,80,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Age</text><text x=\"210\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(80,80,80,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Address</text><text x=\"410\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(80,80,80,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Tag</text><text x=\"510\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(80,80,80,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Action</text><text x=\"10\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">John</text><text x=\"10\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Brown</text><text x=\"110\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"210\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">New York No. 1 Lake Park</text><text x=\"410\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">nice,</text><text x=\"410\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">developer</text><text x=\"510\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Jim Green</text><text x=\"110\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">42</text><text x=\"210\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">London No. 1 Lake Park</text><text x=\"410\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">wow</text><text x=\"510\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Joe Black</text><text x=\"110\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"210\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Sidney No. 1 Lake Park</text><text x=\"410\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">cool,</text><text x=\"410\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">teacher</text><text x=\"510\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(50,50,50,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text></svg>",
 		},
 		{
-			theme: GetTheme(ThemeVividDark),
+			name:        "dark_theme",
+			theme:       GetTheme(ThemeVividDark),
+			makeOptions: makeDefaultTableChartOptions,
+			result:      "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 600 400\"><path  d=\"M 0 0\nL 600 0\nL 600 35\nL 0 35\nL 0 0\" style=\"stroke-width:0;stroke:none;fill:rgba(38,38,42,1.0)\"/><path  d=\"M 0 35\nL 600 35\nL 600 90\nL 0 90\nL 0 35\" style=\"stroke-width:0;stroke:none;fill:rgba(24,24,28,1.0)\"/><path  d=\"M 0 90\nL 600 90\nL 600 145\nL 0 145\nL 0 90\" style=\"stroke-width:0;stroke:none;fill:rgba(38,38,42,1.0)\"/><path  d=\"M 0 145\nL 600 145\nL 600 200\nL 0 200\nL 0 145\" style=\"stroke-width:0;stroke:none;fill:rgba(24,24,28,1.0)\"/><text x=\"10\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Name</text><text x=\"130\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Age</text><text x=\"250\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Address</text><text x=\"370\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Tag</text><text x=\"490\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Action</text><text x=\"10\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">John Brown</text><text x=\"130\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">New York No.</text><text x=\"250\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">1 Lake Park</text><text x=\"370\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">nice,</text><text x=\"370\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">developer</text><text x=\"490\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Jim Green</text><text x=\"130\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">42</text><text x=\"250\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">London No. 1</text><text x=\"250\" y=\"132\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">wow</text><text x=\"490\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Joe Black</text><text x=\"130\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Sidney No. 1</text><text x=\"250\" y=\"187\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">cool, teacher</text><text x=\"490\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text></svg>",
+		},
+		{
+			name: "cell_modified",
 			makeOptions: func() TableChartOption {
-				return TableChartOption{
-					Header: []string{
-						"Name",
-						"Age",
-						"Address",
-						"Tag",
-						"Action",
-					},
-					Data: [][]string{
-						{
-							"John Brown",
-							"32",
-							"New York No. 1 Lake Park",
-							"nice, developer",
-							"Send Mail",
-						},
-						{
-							"Jim Green	",
-							"42",
-							"London No. 1 Lake Park",
-							"wow",
-							"Send Mail",
-						},
-						{
-							"Joe Black	",
-							"32",
-							"Sidney No. 1 Lake Park",
-							"cool, teacher",
-							"Send Mail",
-						},
-					},
+				opt := makeDefaultTableChartOptions()
+				opt.CellModifier = func(tc TableCell) TableCell {
+					if tc.Column%2 == 0 {
+						tc.FillColor = drawing.ColorWhite
+						tc.FontStyle.FontColor = drawing.ColorBlue
+					} else if tc.Row%2 == 0 {
+						tc.FillColor = drawing.ColorAqua
+						tc.FontStyle.FontColor = drawing.ColorBlack
+					} else {
+						tc.FillColor = drawing.ColorYellow
+						tc.FontStyle.FontColor = drawing.ColorPurple
+					}
+					return tc
 				}
+				return opt
 			},
-			result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 600 400\"><path  d=\"M 0 0\nL 600 0\nL 600 35\nL 0 35\nL 0 0\" style=\"stroke-width:0;stroke:none;fill:rgba(38,38,42,1.0)\"/><path  d=\"M 0 35\nL 600 35\nL 600 90\nL 0 90\nL 0 35\" style=\"stroke-width:0;stroke:none;fill:rgba(24,24,28,1.0)\"/><path  d=\"M 0 90\nL 600 90\nL 600 145\nL 0 145\nL 0 90\" style=\"stroke-width:0;stroke:none;fill:rgba(38,38,42,1.0)\"/><path  d=\"M 0 145\nL 600 145\nL 600 200\nL 0 200\nL 0 145\" style=\"stroke-width:0;stroke:none;fill:rgba(24,24,28,1.0)\"/><text x=\"10\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Name</text><text x=\"130\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Age</text><text x=\"250\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Address</text><text x=\"370\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Tag</text><text x=\"490\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Action</text><text x=\"10\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">John Brown</text><text x=\"130\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">New York No.</text><text x=\"250\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">1 Lake Park</text><text x=\"370\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">nice,</text><text x=\"370\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">developer</text><text x=\"490\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Jim Green</text><text x=\"130\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">42</text><text x=\"250\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">London No. 1</text><text x=\"250\" y=\"132\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">wow</text><text x=\"490\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Joe Black</text><text x=\"130\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Sidney No. 1</text><text x=\"250\" y=\"187\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">cool, teacher</text><text x=\"490\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(216,217,218,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text></svg>",
+			result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 600 400\"><path  d=\"M 0 0\nL 600 0\nL 600 35\nL 0 35\nL 0 0\" style=\"stroke-width:0;stroke:none;fill:rgba(220,220,220,1.0)\"/><path  d=\"M 0 35\nL 600 35\nL 600 90\nL 0 90\nL 0 35\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 0 90\nL 600 90\nL 600 145\nL 0 145\nL 0 90\" style=\"stroke-width:0;stroke:none;fill:rgba(245,245,245,1.0)\"/><path  d=\"M 0 145\nL 600 145\nL 600 200\nL 0 200\nL 0 145\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 10 10\nL 110 10\nL 110 25\nL 10 25\nL 10 10\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 130 10\nL 230 10\nL 230 25\nL 130 25\nL 130 10\" style=\"stroke-width:0;stroke:none;fill:rgba(0,255,255,1.0)\"/><path  d=\"M 250 10\nL 350 10\nL 350 25\nL 250 25\nL 250 10\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 370 10\nL 470 10\nL 470 25\nL 370 25\nL 370 10\" style=\"stroke-width:0;stroke:none;fill:rgba(0,255,255,1.0)\"/><path  d=\"M 490 10\nL 590 10\nL 590 25\nL 490 25\nL 490 10\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 10 45\nL 110 45\nL 110 80\nL 10 80\nL 10 45\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 130 45\nL 230 45\nL 230 80\nL 130 80\nL 130 45\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,0,1.0)\"/><path  d=\"M 250 45\nL 350 45\nL 350 80\nL 250 80\nL 250 45\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 370 45\nL 470 45\nL 470 80\nL 370 80\nL 370 45\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,0,1.0)\"/><path  d=\"M 490 45\nL 590 45\nL 590 80\nL 490 80\nL 490 45\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 10 100\nL 110 100\nL 110 135\nL 10 135\nL 10 100\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 130 100\nL 230 100\nL 230 135\nL 130 135\nL 130 100\" style=\"stroke-width:0;stroke:none;fill:rgba(0,255,255,1.0)\"/><path  d=\"M 250 100\nL 350 100\nL 350 135\nL 250 135\nL 250 100\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 370 100\nL 470 100\nL 470 135\nL 370 135\nL 370 100\" style=\"stroke-width:0;stroke:none;fill:rgba(0,255,255,1.0)\"/><path  d=\"M 490 100\nL 590 100\nL 590 135\nL 490 135\nL 490 100\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 10 155\nL 110 155\nL 110 190\nL 10 190\nL 10 155\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 130 155\nL 230 155\nL 230 190\nL 130 190\nL 130 155\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,0,1.0)\"/><path  d=\"M 250 155\nL 350 155\nL 350 190\nL 250 190\nL 250 155\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path  d=\"M 370 155\nL 470 155\nL 470 190\nL 370 190\nL 370 155\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,0,1.0)\"/><path  d=\"M 490 155\nL 590 155\nL 590 190\nL 490 190\nL 490 155\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><text x=\"10\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Name</text><text x=\"130\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Age</text><text x=\"250\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Address</text><text x=\"370\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Tag</text><text x=\"490\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Action</text><text x=\"10\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">John Brown</text><text x=\"130\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(128,0,128,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">New York No.</text><text x=\"250\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">1 Lake Park</text><text x=\"370\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(128,0,128,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">nice,</text><text x=\"370\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(128,0,128,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">developer</text><text x=\"490\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Jim Green</text><text x=\"130\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">42</text><text x=\"250\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">London No. 1</text><text x=\"250\" y=\"132\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,0,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">wow</text><text x=\"490\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Joe Black</text><text x=\"130\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(128,0,128,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Sidney No. 1</text><text x=\"250\" y=\"187\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(128,0,128,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">cool, teacher</text><text x=\"490\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(0,0,255,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text></svg>",
 		},
 	}
 
diff --git a/theme_test.go b/theme_test.go
index 7c80aad..ea3ff4b 100644
--- a/theme_test.go
+++ b/theme_test.go
@@ -205,3 +205,55 @@ func TestWithTextColor(t *testing.T) {
 	assert.Equal(t, drawing.ColorWhite, blackCP.GetBackgroundColor())
 	assert.Equal(t, drawing.ColorWhite, blackCP.GetSeriesColor(0))
 }
+
+func TestWithSeriesColors(t *testing.T) {
+	t.Parallel()
+
+	yellowCP := &themeColorPalette{
+		name:               t.Name(),
+		isDarkMode:         false,
+		axisSplitLineColor: drawing.ColorYellow,
+		axisStrokeColor:    drawing.ColorYellow,
+		backgroundColor:    drawing.ColorYellow,
+		textColor:          drawing.ColorYellow,
+		seriesColors:       []Color{drawing.ColorYellow},
+	}
+
+	t.Run("ignored", func(t *testing.T) {
+		invalidCP := yellowCP.WithSeriesColors([]Color{})
+
+		assert.Equal(t, yellowCP.GetSeriesColor(0), invalidCP.GetSeriesColor(0))
+	})
+	t.Run("updated", func(t *testing.T) {
+		blackCP := yellowCP.WithSeriesColors([]Color{drawing.ColorBlack})
+
+		assert.Equal(t, drawing.ColorYellow, blackCP.GetTextColor())
+		assert.Equal(t, drawing.ColorYellow, blackCP.GetAxisSplitLineColor())
+		assert.Equal(t, drawing.ColorYellow, blackCP.GetAxisStrokeColor())
+		assert.Equal(t, drawing.ColorYellow, blackCP.GetBackgroundColor())
+		assert.Equal(t, drawing.ColorBlack, blackCP.GetSeriesColor(0))
+	})
+}
+
+func TestWithBackgroundColor(t *testing.T) {
+	t.Parallel()
+
+	whiteCP := &themeColorPalette{
+		name:               t.Name(),
+		isDarkMode:         false,
+		axisSplitLineColor: drawing.ColorWhite,
+		axisStrokeColor:    drawing.ColorWhite,
+		backgroundColor:    drawing.ColorWhite,
+		textColor:          drawing.ColorWhite,
+		seriesColors:       []Color{drawing.ColorWhite},
+	}
+
+	blackCP := whiteCP.WithBackgroundColor(drawing.ColorBlack)
+
+	require.False(t, whiteCP.IsDark())
+	assert.True(t, blackCP.IsDark())
+
+	yellowCP := blackCP.WithBackgroundColor(drawing.ColorYellow)
+
+	require.False(t, yellowCP.IsDark())
+}
diff --git a/util_test.go b/util_test.go
index 9c51e6a..92e4a07 100644
--- a/util_test.go
+++ b/util_test.go
@@ -73,10 +73,10 @@ func TestMeasureTextMaxWidthHeight(t *testing.T) {
 		Height: 300,
 	})
 	require.NoError(t, err)
-	style := chartdraw.Style{
+	style := FontStyle{
 		FontSize: 10,
 	}
-	p.SetStyle(style)
+	p.SetStyle(chartdraw.Style{FontStyle: style})
 
 	maxWidth, maxHeight := measureTextMaxWidthHeight([]string{
 		"Mon",