Repository: microcosm-cc/bluemonday
Pull request #83 (2019-07-11 23:21:43)

Add Css Styling for element attributes
Let me know if anything needs adjusting or if you have any questions about this, I'll try to respond quickly.


Patch:
diff --git a/README.md b/README.md
index 8d8a6b2..ced556a 100644
--- a/README.md
+++ b/README.md
@@ -92,7 +92,7 @@ func main() {
 	// Do this once for each unique policy, and use the policy for the life of the program
 	// Policy creation/editing is not safe to use in multiple goroutines
 	p := bluemonday.UGCPolicy()
-	
+
 	// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines
 	html := p.Sanitize(
 		`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
@@ -204,6 +204,40 @@ p := bluemonday.UGCPolicy()
 p.AllowElements("fieldset", "select", "option")
 ```
 
+### Inline CSS
+
+Although it's possible to handle inline CSS using `AllowAttrs` with a `Matching` rule, writing a single monolithic regular expression to safely process all inline CSS which you wish to allow is not a trivial task.  Instead of attempting to do so, you can whitelist the `style` attribute on whichever element(s) you desire and use style policies to control and sanitize inline styles.
+
+It is suggested that you use `Matching` (with a suitable regular expression)
+`MatchingEnum`, or `MatchingHandler` to ensure each style matches your needs,
+but default handlers are supplied for most widely used styles.
+
+Similar to attributes, you can allow specific CSS properties to be set inline:
+```go
+p.AllowAttrs("style").OnElements("span", "p")
+// Allow the 'color' property with valid RGB(A) hex values only (on any element allowed a 'style' attribute)
+p.AllowStyles("color").Matching(regexp.MustCompile("(?i)^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$")).Globally()
+```
+
+Additionally, you can allow a CSS property to be set only to an allowed value:
+```go
+p.AllowAttrs("style").OnElements("span", "p")
+// Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none'
+// on 'span' elements only
+p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElements("span")
+```
+If you need more specific checking, you can create a handler that takes in a string and returns a bool to
+validate the values for a given property. The string parameter has been
+converted to lowercase and unicode code points have been converted.
+```go
+myHandler := func(value string) bool{
+	return true
+}
+p.AllowAttrs("style").OnElements("span", "p")
+// Allow the 'color' property with values validated by the handler (on any element allowed a 'style' attribute)
+p.AllowStyles("color").MatchingHandler(myHandler).Globally()
+```
+
 ### Links
 
 Links are difficult beasts to sanitise safely and also one of the biggest attack vectors for malicious content.
diff --git a/go.mod b/go.mod
index fa8453c..69940a1 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,9 @@ module github.com/microcosm-cc/bluemonday
 
 go 1.9
 
-require golang.org/x/net v0.0.0-20181220203305-927f97764cc3
+require (
+	github.com/aymerick/douceur v0.2.0
+	github.com/chris-ramon/douceur v0.2.0
+	github.com/gorilla/css v1.0.0 // indirect
+	golang.org/x/net v0.0.0-20181220203305-927f97764cc3
+)
diff --git a/go.sum b/go.sum
index bee241d..8c34e7a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,8 @@
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
+github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
+github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/handlers.go b/handlers.go
new file mode 100644
index 0000000..807f009
--- /dev/null
+++ b/handlers.go
@@ -0,0 +1,2085 @@
+// Copyright (c) 2019, David Kitchen <david@buro9.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+//   list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+//
+// * Neither the name of the organisation (Microcosm) nor the names of its
+//   contributors may be used to endorse or promote products derived from
+//   this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package bluemonday
+
+import (
+	"regexp"
+	"strings"
+)
+
+var (
+	defaultStyleHandlers = map[string]func(string) bool{
+		"align-content":              AlignContentHandler,
+		"align-items":                AlignItemsHandler,
+		"align-self":                 AlignSelfHandler,
+		"all":                        AllHandler,
+		"animation":                  AnimationHandler,
+		"animation-delay":            AnimationDelayHandler,
+		"animation-direction":        AnimationDirectionHandler,
+		"animation-duration":         AnimationDurationHandler,
+		"animation-fill-mode":        AnimationFillModeHandler,
+		"animation-iteration-count":  AnimationIterationCountHandler,
+		"animation-name":             AnimationNameHandler,
+		"animation-play-state":       AnimationPlayStateHandler,
+		"animation-timing-function":  TimingFunctionHandler,
+		"backface-visibility":        BackfaceVisibilityHandler,
+		"background":                 BackgroundHandler,
+		"background-attachment":      BackgroundAttachmentHandler,
+		"background-blend-mode":      BackgroundBlendModeHandler,
+		"background-clip":            BackgroundClipHandler,
+		"background-color":           ColorHandler,
+		"background-image":           ImageHandler,
+		"background-origin":          BackgroundOriginHandler,
+		"background-position":        BackgroundPositionHandler,
+		"background-repeat":          BackgroundRepeatHandler,
+		"background-size":            BackgroundSizeHandler,
+		"border":                     BorderHandler,
+		"border-bottom":              BorderSideHandler,
+		"border-bottom-color":        ColorHandler,
+		"border-bottom-left-radius":  BorderSideRadiusHandler,
+		"border-bottom-right-radius": BorderSideRadiusHandler,
+		"border-bottom-style":        BorderSideStyleHandler,
+		"border-bottom-width":        BorderSideWidthHandler,
+		"border-collapse":            BorderCollapseHandler,
+		"border-color":               ColorHandler,
+		"border-image":               BorderImageHandler,
+		"border-image-outset":        BorderImageOutsetHandler,
+		"border-image-repeat":        BorderImageRepeatHandler,
+		"border-image-slice":         BorderImageSliceHandler,
+		"border-image-source":        ImageHandler,
+		"border-image-width":         BorderImageWidthHandler,
+		"border-left":                BorderSideHandler,
+		"border-left-color":          ColorHandler,
+		"border-left-style":          BorderSideStyleHandler,
+		"border-left-width":          BorderSideWidthHandler,
+		"border-radius":              BorderRadiusHandler,
+		"border-right":               BorderSideHandler,
+		"border-right-color":         ColorHandler,
+		"border-right-style":         BorderSideStyleHandler,
+		"border-right-width":         BorderSideWidthHandler,
+		"border-spacing":             BorderSpacingHandler,
+		"border-style":               BorderStyleHandler,
+		"border-top":                 BorderSideHandler,
+		"border-top-color":           ColorHandler,
+		"border-top-left-radius":     BorderSideRadiusHandler,
+		"border-top-right-radius":    BorderSideRadiusHandler,
+		"border-top-style":           BorderSideStyleHandler,
+		"border-top-width":           BorderSideWidthHandler,
+		"border-width":               BorderWidthHandler,
+		"bottom":                     SideHandler,
+		"box-decoration-break":       BoxDecorationBreakHandler,
+		"box-shadow":                 BoxShadowHandler,
+		"box-sizing":                 BoxSizingHandler,
+		"break-after":                BreakBeforeAfterHandler,
+		"break-before":               BreakBeforeAfterHandler,
+		"break-inside":               BreakInsideHandler,
+		"caption-side":               CaptionSideHandler,
+		"caret-color":                CaretColorHandler,
+		"clear":                      ClearHandler,
+		"clip":                       ClipHandler,
+		"color":                      ColorHandler,
+		"column-count":               ColumnCountHandler,
+		"column-fill":                ColumnFillHandler,
+		"column-gap":                 ColumnGapHandler,
+		"column-rule":                ColumnRuleHandler,
+		"column-rule-color":          ColorHandler,
+		"column-rule-style":          BorderSideStyleHandler,
+		"column-rule-width":          ColumnRuleWidthHandler,
+		"column-span":                ColumnSpanHandler,
+		"column-width":               ColumnWidthHandler,
+		"columns":                    ColumnsHandler,
+		"cursor":                     CursorHandler,
+		"direction":                  DirectionHandler,
+		"display":                    DisplayHandler,
+		"empty-cells":                EmptyCellsHandler,
+		"filter":                     FilterHandler,
+		"flex":                       FlexHandler,
+		"flex-basis":                 FlexBasisHandler,
+		"flex-direction":             FlexDirectionHandler,
+		"flex-flow":                  FlexFlowHandler,
+		"flex-grow":                  FlexGrowHandler,
+		"flex-shrink":                FlexGrowHandler,
+		"flex-wrap":                  FlexWrapHandler,
+		"float":                      FloatHandler,
+		"font":                       FontHandler,
+		"font-family":                FontFamilyHandler,
+		"font-kerning":               FontKerningHandler,
+		"font-language-override":     FontLanguageOverrideHandler,
+		"font-size":                  FontSizeHandler,
+		"font-size-adjust":           FontSizeAdjustHandler,
+		"font-stretch":               FontStretchHandler,
+		"font-style":                 FontStyleHandler,
+		"font-synthesis":             FontSynthesisHandler,
+		"font-variant":               FontVariantHandler,
+		"font-variant-caps":          FontVariantCapsHandler,
+		"font-variant-position":      FontVariantPositionHandler,
+		"font-weight":                FontWeightHandler,
+		"grid":                       GridHandler,
+		"grid-area":                  GridAreaHandler,
+		"grid-auto-columns":          GridAutoColumnsHandler,
+		"grid-auto-flow":             GridAutoFlowHandler,
+		"grid-auto-rows":             GridAutoColumnsHandler,
+		"grid-column":                GridColumnHandler,
+		"grid-column-end":            GridAxisStartEndHandler,
+		"grid-column-gap":            LengthHandler,
+		"grid-column-start":          GridAxisStartEndHandler,
+		"grid-gap":                   GridGapHandler,
+		"grid-row":                   GridRowHandler,
+		"grid-row-end":               GridAxisStartEndHandler,
+		"grid-row-gap":               LengthHandler,
+		"grid-row-start":             GridAxisStartEndHandler,
+		"grid-template":              GridTemplateHandler,
+		"grid-template-areas":        GridTemplateAreasHandler,
+		"grid-template-columns":      GridTemplateColumnsHandler,
+		"grid-template-rows":         GridTemplateRowsHandler,
+		"hanging-punctuation":        HangingPunctuationHandler,
+		"height":                     HeightHandler,
+		"hyphens":                    HyphensHandler,
+		"image-rendering":            ImageRenderingHandler,
+		"isolation":                  IsolationHandler,
+		"justify-content":            JustifyContentHandler,
+		"left":                       SideHandler,
+		"letter-spacing":             LetterSpacingHandler,
+		"line-break":                 LineBreakHandler,
+		"line-height":                LineHeightHandler,
+		"list-style":                 ListStyleHandler,
+		"list-style-image":           ImageHandler,
+		"list-style-position":        ListStylePositionHandler,
+		"list-style-type":            ListStyleTypeHandler,
+		"margin":                     MarginHandler,
+		"margin-bottom":              MarginSideHandler,
+		"margin-left":                MarginSideHandler,
+		"margin-right":               MarginSideHandler,
+		"margin-top":                 MarginSideHandler,
+		"max-height":                 MaxHeightWidthHandler,
+		"max-width":                  MaxHeightWidthHandler,
+		"min-height":                 MinHeightWidthHandler,
+		"min-width":                  MinHeightWidthHandler,
+		"mix-blend-mode":             MixBlendModeHandler,
+		"object-fit":                 ObjectFitHandler,
+		"object-position":            ObjectPositionHandler,
+		"opacity":                    OpacityHandler,
+		"order":                      OrderHandler,
+		"orphans":                    OrphansHandler,
+		"outline":                    OutlineHandler,
+		"outline-color":              ColorHandler,
+		"outline-offset":             OutlineOffsetHandler,
+		"outline-style":              OutlineStyleHandler,
+		"outline-width":              OutlineWidthHandler,
+		"overflow":                   OverflowHandler,
+		"overflow-wrap":              OverflowWrapHandler,
+		"overflow-x":                 OverflowXYHandler,
+		"overflow-y":                 OverflowXYHandler,
+		"padding":                    PaddingHandler,
+		"padding-bottom":             PaddingSideHandler,
+		"padding-left":               PaddingSideHandler,
+		"padding-right":              PaddingSideHandler,
+		"padding-top":                PaddingSideHandler,
+		"page-break-after":           PageBreakBeforeAfterHandler,
+		"page-break-before":          PageBreakBeforeAfterHandler,
+		"page-break-inside":          PageBreakInsideHandler,
+		"perspective":                PerspectiveHandler,
+		"perspective-origin":         PerspectiveOriginHandler,
+		"pointer-events":             PointerEventsHandler,
+		"position":                   PositionHandler,
+		"quotes":                     QuotesHandler,
+		"resize":                     ResizeHandler,
+		"right":                      SideHandler,
+		"scroll-behavior":            ScrollBehaviorHandler,
+		"tab-size":                   TabSizeHandler,
+		"table-layout":               TableLayoutHandler,
+		"text-align":                 TextAlignHandler,
+		"text-align-last":            TextAlignLastHandler,
+		"text-combine-upright":       TextCombineUprightHandler,
+		"text-decoration":            TextDecorationHandler,
+		"text-decoration-color":      ColorHandler,
+		"text-decoration-line":       TextDecorationLineHandler,
+		"text-decoration-style":      TextDecorationStyleHandler,
+		"text-indent":                TextIndentHandler,
+		"text-justify":               TextJustifyHandler,
+		"text-orientation":           TextOrientationHandler,
+		"text-overflow":              TextOverflowHandler,
+		"text-shadow":                TextShadowHandler,
+		"text-transform":             TextTransformHandler,
+		"top":                        SideHandler,
+		"transform":                  TransformHandler,
+		"transform-origin":           TransformOriginHandler,
+		"transform-style":            TransformStyleHandler,
+		"transition":                 TransitionHandler,
+		"transition-delay":           TransitionDelayHandler,
+		"transition-duration":        TransitionDurationHandler,
+		"transition-property":        TransitionPropertyHandler,
+		"transition-timing-function": TimingFunctionHandler,
+		"unicode-bidi":               UnicodeBidiHandler,
+		"user-select":                UserSelectHandler,
+		"vertical-align":             VerticalAlignHandler,
+		"visibility":                 VisiblityHandler,
+		"white-space":                WhiteSpaceHandler,
+		"widows":                     OrphansHandler,
+		"width":                      WidthHandler,
+		"word-break":                 WordBreakHandler,
+		"word-spacing":               WordSpacingHandler,
+		"word-wrap":                  WordWrapHandler,
+		"writing-mode":               WritingModeHandler,
+		"z-index":                    ZIndexHandler,
+	}
+	colorValues = []string{"initial", "inherit", "aliceblue", "antiquewhite",
+		"aqua", "aquamarine", "azure", "beige", "bisque", "black",
+		"blanchedalmond", "blue", "blueviolet", "brown", "burlywood",
+		"cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+		"cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+		"darkgray", "darkgrey", "darkgreen", "darkkhaki", "darkmagenta",
+		"darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
+		"darkseagreen", "darkslateblue", "darkslategrey", "darkslategray",
+		"darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray",
+		"dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen",
+		"fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
+		"grey", "green", "greenyellow", "honeydew", "hotpink", "indianred",
+		"indigo", "ivory", "khaki", "lavender", "lavenderblush",
+		"lemonchiffon", "lightblue", "lightcoral", "lightcyan",
+		"lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen",
+		"lightpink", "lightsalmon", "lightseagreen", "lightskyblue",
+		"lightslategray", "lightslategrey", "lightsteeelblue", "lightyellow",
+		"lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
+		"mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
+		"mediumslateblue", "mediumspringgreen", "mediumturquoise",
+		"mediumvioletred", "midnightblue", "mintcream", "mistyrose",
+		"moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
+		"orange", "orangered", "orchid", "palegoldenrod", "palegreen",
+		"paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru",
+		"pink", "plum", "powderblue", "purple", "rebeccapurple", "red",
+		"rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown",
+		"seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue",
+		"slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
+		"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+		"whitesmoke", "yellow", "yellowgreen"}
+)
+
+func multiSplit(value string, seps ...string) []string {
+	curArray := []string{value}
+	for _, i := range seps {
+		newArray := []string{}
+		for _, j := range curArray {
+			newArray = append(newArray, strings.Split(j, i)...)
+		}
+		curArray = newArray
+	}
+	return curArray
+}
+
+func recursiveCheck(value []string, funcs []func(string) bool) bool {
+	for i := 0; i < len(value); i++ {
+		tempVal := strings.Join(value[:i+1], " ")
+		for _, j := range funcs {
+			if j(tempVal) && (len(value[i+1:]) == 0 || recursiveCheck(value[i+1:], funcs)) {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func in(value []string, arr []string) bool {
+	for _, i := range value {
+		foundString := false
+		for _, j := range arr {
+			if j == i {
+				foundString = true
+			}
+		}
+		if !foundString {
+			return false
+		}
+	}
+	return true
+}
+
+func splitValues(value string) []string {
+	values := strings.Split(value, ",")
+	for _, strippedValue := range values {
+		strippedValue = strings.ToLower(strings.TrimSpace(strippedValue))
+	}
+	return values
+}
+
+func getDefaultHandler(attr string) func(string) bool {
+
+	if defaultStyleHandlers[attr] != nil {
+		return defaultStyleHandlers[attr]
+	}
+	return BaseHandler
+}
+
+func BaseHandler(value string) bool {
+	return false
+}
+
+func AlignContentHandler(value string) bool {
+	values := []string{"stretch", "center", "flex-start",
+		"flex-end", "space-between", "space-around", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AlignItemsHandler(value string) bool {
+	values := []string{"stretch", "center", "flex-start",
+		"flex-end", "baseline", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AlignSelfHandler(value string) bool {
+	values := []string{"auto", "stretch", "center", "flex-start",
+		"flex-end", "baseline", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AllHandler(value string) bool {
+	values := []string{"initial", "inherit", "unset"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AnimationHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		AnimationNameHandler,
+		AnimationDurationHandler,
+		TimingFunctionHandler,
+		AnimationDelayHandler,
+		AnimationIterationCountHandler,
+		AnimationDirectionHandler,
+		AnimationFillModeHandler,
+		AnimationPlayStateHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func AnimationDelayHandler(value string) bool {
+	reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*[s|ms]?`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AnimationDirectionHandler(value string) bool {
+	values := []string{"normal", "reverse", "alternate", "alternate-reverse", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AnimationDurationHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*[s|ms]?`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AnimationFillModeHandler(value string) bool {
+	values := []string{"none", "forwards", "backwards", "both", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AnimationIterationCountHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"infinite", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func AnimationNameHandler(value string) bool {
+	reg := regexp.MustCompile(`[a-z]+`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func AnimationPlayStateHandler(value string) bool {
+	values := []string{"paused", "running", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TimingFunctionHandler(value string) bool {
+	values := []string{"linear", "ease", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "initial", "inherit"}
+	splitVals := splitValues(value)
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func BackfaceVisibilityHandler(value string) bool {
+	values := []string{"visible", "hidden", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BackgroundHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	newSplitVals := []string{}
+	for _, i := range splitVals {
+		if len(strings.Split(i, "/")) == 2 {
+			newSplitVals = append(newSplitVals, strings.Split(i, "/")...)
+		} else {
+			newSplitVals = append(newSplitVals, i)
+		}
+	}
+	usedFunctions := []func(string) bool{
+		ColorHandler,
+		ImageHandler,
+		BackgroundPositionHandler,
+		BackgroundSizeHandler,
+		BackgroundRepeatHandler,
+		BackgroundOriginHandler,
+		BackgroundClipHandler,
+		BackgroundAttachmentHandler,
+	}
+	return recursiveCheck(newSplitVals, usedFunctions)
+}
+
+func BackgroundAttachmentHandler(value string) bool {
+	values := []string{"scroll", "fixed", "local", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BackgroundClipHandler(value string) bool {
+	values := []string{"border-box", "padding-box", "content-box", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BackgroundBlendModeHandler(value string) bool {
+	values := []string{"normal", "multiply", "screen", "overlay", "darken",
+		"lighten", "color-dodge", "saturation", "color", "luminosity"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ImageHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func BackgroundOriginHandler(value string) bool {
+	values := []string{"padding-box", "border-box", "content-box", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BackgroundPositionHandler(value string) bool {
+	splitVals := strings.Split(value, ";")
+	values := []string{"left", "left top", "left bottom", "right", "right top", "right bottom", "right center", "center top", "center center", "center bottom", "center", "top", "bottom", "initial", "inherit"}
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	return false
+}
+
+func BackgroundRepeatHandler(value string) bool {
+	values := []string{"repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BackgroundSizeHandler(value string) bool {
+	splitVals := strings.Split(value, " ")
+	values := []string{"auto", "cover", "contain", "initial", "inherit"}
+	if in(splitVals, values) {
+		return true
+	}
+	if len(splitVals) > 0 && LengthHandler(splitVals[0]) {
+		if len(splitVals) < 2 || (len(splitVals) == 2 && LengthHandler(splitVals[1])) {
+			return true
+		}
+	}
+	return false
+}
+
+func BorderHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := multiSplit(value, " ", "/")
+	usedFunctions := []func(string) bool{
+		BorderWidthHandler,
+		BorderStyleHandler,
+		ColorHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderSideHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		BorderSideWidthHandler,
+		BorderSideStyleHandler,
+		ColorHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderSideRadiusHandler(value string) bool {
+	splitVals := strings.Split(value, " ")
+	valid := true
+	for _, i := range splitVals {
+		if !LengthHandler(i) {
+			valid = false
+			break
+		}
+	}
+	if valid {
+		return true
+	}
+	splitVals = splitValues(value)
+	values := []string{"initial", "inherit"}
+	return in(splitVals, values)
+}
+
+func BorderSideStyleHandler(value string) bool {
+	values := []string{"none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BorderSideWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	splitVals := strings.Split(value, ";")
+	values := []string{"medium", "thin", "thick", "initial", "inherit"}
+	return in(splitVals, values)
+}
+
+func BorderCollapseHandler(value string) bool {
+	values := []string{"separate", "collapse", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BorderImageHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := multiSplit(value, " ", " / ")
+	usedFunctions := []func(string) bool{
+		ImageHandler,
+		BorderImageSliceHandler,
+		BorderImageWidthHandler,
+		BorderImageOutsetHandler,
+		BorderImageRepeatHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderImageOutsetHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BorderImageRepeatHandler(value string) bool {
+	values := []string{"stretch", "repeat", "round", "space", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BorderImageSliceHandler(value string) bool {
+	values := []string{"fill", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 4 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		LengthHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderImageWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BorderRadiusHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 4 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		LengthHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderSpacingHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 2 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		LengthHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderStyleHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 4 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		BorderSideStyleHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func BorderWidthHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 4 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		BorderSideWidthHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func SideHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"auto", "inherit", "unset"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BoxDecorationBreakHandler(value string) bool {
+	values := []string{"slice", "clone", "initial", "initial", "inherit", "unset"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BoxShadowHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	commaSplitVals := strings.Split(value, ",")
+	for _, val := range commaSplitVals {
+		splitVals := strings.Split(val, " ")
+		if len(splitVals) > 6 || len(splitVals) < 2 {
+			return false
+		}
+		if !LengthHandler(splitVals[0]) {
+			return false
+		}
+		if !LengthHandler(splitVals[1]) {
+			return false
+		}
+		usedFunctions := []func(string) bool{
+			LengthHandler,
+			ColorHandler,
+		}
+		if len(splitVals) > 2 && !recursiveCheck(splitVals[2:], usedFunctions) {
+			return false
+		}
+	}
+	return true
+}
+
+func BoxSizingHandler(value string) bool {
+	values := []string{"slicontent-box", "border-box", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BreakBeforeAfterHandler(value string) bool {
+	values := []string{"auto", "avoid", "always", "all", "avoid-page", "page", "left", "right", "recto", "verso", "avoid-column", "column", "avoid-region", "region"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func BreakInsideHandler(value string) bool {
+	values := []string{"auto", "avoid", "avoid-page", "avoid-column", "avoid-region"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func CaptionSideHandler(value string) bool {
+	values := []string{"top", "bottom", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func CaretColorHandler(value string) bool {
+	splitVals := splitValues(value)
+	if in(splitVals, colorValues) {
+		return true
+	}
+	reg := regexp.MustCompile(`#[0-9abcdef]{6}`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`)
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	return false
+}
+
+func ClearHandler(value string) bool {
+	values := []string{"none", "left", "right", "both", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ClipHandler(value string) bool {
+	reg := regexp.MustCompile(`rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ColorHandler(value string) bool {
+	splitVals := splitValues(value)
+	if in(splitVals, colorValues) {
+		return true
+	}
+	reg := regexp.MustCompile(`#[0-9abcdef]{6}`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	return false
+}
+
+func ColumnCountHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9]+`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ColumnFillHandler(value string) bool {
+	values := []string{"balance", "auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ColumnGapHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"normal", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ColumnRuleHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		ColumnRuleWidthHandler,
+		BorderSideStyleHandler,
+		ColorHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func ColumnRuleWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	splitVals := strings.Split(value, ";")
+	values := []string{"medium", "thin", "thick", "initial", "inherit"}
+	return in(splitVals, values)
+}
+
+func ColumnSpanHandler(value string) bool {
+	values := []string{"none", "all", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ColumnWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	splitVals := strings.Split(value, ";")
+	values := []string{"auto", "initial", "inherit"}
+	return in(splitVals, values)
+}
+
+func ColumnsHandler(value string) bool {
+	values := []string{"auto", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		ColumnWidthHandler,
+		ColumnCountHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func CursorHandler(value string) bool {
+	values := []string{"alias", "all-scroll", "auto", "cell", "context-menu", "col-resize", "copy", "crosshair", "default", "e-resize", "ew-resize", "grab", "grabbing", "help", "move", "n-resize", "ne-resize", "nesw-resize", "ns-resize", "nw-resize", "nwse-resize", "no-drop", "none", "not-allowed", "pointer", "progress", "row-resize", "s-resize", "se-resize", "sw-resize", "text", "vertical-text", "w-resize", "wait", "zoom-in", "zoom-out", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func DirectionHandler(value string) bool {
+	values := []string{"ltr", "rtl", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func DisplayHandler(value string) bool {
+	values := []string{"inline", "block", "contents", "flex", "grid", "inline-block", "inline-flex", "inline-grid", "inline-table", "list-item", "run-in", "table", "table-caption", "table-column-group", "table-header-group", "table-footer-group", "table-row-group", "table-cell", "table-column", "table-row", "none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func EmptyCellsHandler(value string) bool {
+	values := []string{"show", "hide", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FilterHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`blur\([0-9]+px\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`(brightness|contrast)\([0-9]+\%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`)
+	reg.Longest()
+	colorValue := strings.TrimSuffix(string(reg.ReplaceAll([]byte(value), []byte{})), ")")
+	if ColorHandler(colorValue) {
+		return true
+	}
+	reg = regexp.MustCompile(`grayscale\(([0-9]{1,2}|100)%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`invert\(([0-9]{1,2}|100)%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`opacity\(([0-9]{1,2}|100)%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`saturate\([0-9]+%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`sepia\(([0-9]{1,2}|100)%\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	//Not allowing URLs
+	return false
+}
+
+func FlexHandler(value string) bool {
+	values := []string{"auto", "initial", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		FlexGrowHandler,
+		FlexBasisHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func FlexBasisHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	splitVals := strings.Split(value, ";")
+	values := []string{"auto", "initial", "inherit"}
+	return in(splitVals, values)
+}
+
+func FlexDirectionHandler(value string) bool {
+	values := []string{"row", "row-reverse", "column", "column-reverse", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FlexFlowHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		FlexDirectionHandler,
+		FlexWrapHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func FlexGrowHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9\.]+`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	splitVals := strings.Split(value, ";")
+	values := []string{"initial", "inherit"}
+	return in(splitVals, values)
+}
+
+func FlexWrapHandler(value string) bool {
+	values := []string{"nowrap", "wrap", "wrap-reverse", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FloatHandler(value string) bool {
+	values := []string{"none", "left", "right", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontHandler(value string) bool {
+	values := []string{"caption", "icon", "menu", "message-box", "small-caption", "status-bar", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	newSplitVals := []string{}
+	for _, i := range splitVals {
+		if len(strings.Split(i, "/")) == 2 {
+			newSplitVals = append(newSplitVals, strings.Split(i, "/")...)
+		} else {
+			newSplitVals = append(newSplitVals, i)
+		}
+	}
+	usedFunctions := []func(string) bool{
+		FontStyleHandler,
+		FontVariantHandler,
+		FontWeightHandler,
+		FontSizeHandler,
+		FontFamilyHandler,
+	}
+	return recursiveCheck(newSplitVals, usedFunctions)
+}
+
+func FontFamilyHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`('[a-z ]+'|[a-z]+)`)
+	reg.Longest()
+	for _, i := range splitVals {
+		i = strings.TrimSpace(i)
+		if reg.FindString(i) != i {
+			return false
+		}
+	}
+	return true
+}
+
+func FontKerningHandler(value string) bool {
+	values := []string{"auto", "normal", "none"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontLanguageOverrideHandler(value string) bool {
+	reg := regexp.MustCompile(`[a-z]+`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func FontSizeHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"medium", "xx-small", "x-small", "small", "large", "x-large", "xx-large", "smaller", "larger", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontSizeAdjustHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontStretchHandler(value string) bool {
+	values := []string{"ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontStyleHandler(value string) bool {
+	values := []string{"normal", "italic", "oblique", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontSynthesisHandler(value string) bool {
+	values := []string{"none", "style", "weight"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontVariantCapsHandler(value string) bool {
+	values := []string{"normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontVariantHandler(value string) bool {
+	values := []string{"normal", "small-caps", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontVariantPositionHandler(value string) bool {
+	values := []string{"normal", "sub", "super"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func FontWeightHandler(value string) bool {
+	values := []string{"normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func GridHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	newSplitVals := []string{}
+	for _, i := range splitVals {
+		if i != "/" {
+			newSplitVals = append(newSplitVals, i)
+		}
+	}
+	usedFunctions := []func(string) bool{
+		GridTemplateRowsHandler,
+		GridTemplateColumnsHandler,
+		GridTemplateAreasHandler,
+		GridAutoColumnsHandler,
+		GridAutoFlowHandler,
+	}
+	return recursiveCheck(newSplitVals, usedFunctions)
+}
+
+func GridAreaHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " / ")
+	usedFunctions := []func(string) bool{
+		GridAxisStartEndHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func GridAutoColumnsHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"auto", "max-content", "min-content", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func GridAutoFlowHandler(value string) bool {
+	values := []string{"row", "column", "dense", "row dense", "column dense"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func GridColumnHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " / ")
+	if len(splitVals) > 2 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		GridAxisStartEndHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func GridColumnGapHandler(value string) bool {
+	return LengthHandler(value)
+}
+
+func LengthHandler(value string) bool {
+	reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func LineBreakHandler(value string) bool {
+	values := []string{"auto", "loose", "normal", "strict"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func GridAxisStartEndHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9]+`)
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`span [0-9]+`)
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"auto"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func GridGapHandler(value string) bool {
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 2 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		GridColumnGapHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func GridRowHandler(value string) bool {
+	splitVals := strings.Split(value, " / ")
+	if len(splitVals) > 2 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		GridAxisStartEndHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func GridTemplateHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " / ")
+	if len(splitVals) > 2 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		GridTemplateColumnsHandler,
+		GridTemplateRowsHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func GridTemplateAreasHandler(value string) bool {
+	values := []string{"none"}
+	if in([]string{value}, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`['"]?[a-z ]+['"]?`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func GridTemplateColumnsHandler(value string) bool {
+	splitVals := strings.Split(value, " ")
+	values := []string{"none", "auto", "max-content", "min-content", "initial", "inherit"}
+	for _, val := range splitVals {
+		if LengthHandler(val) {
+			continue
+		}
+		valArr := []string{val}
+		if !in(valArr, values) {
+			return false
+		}
+	}
+	return true
+}
+
+func GridTemplateRowsHandler(value string) bool {
+	splitVals := strings.Split(value, " ")
+	values := []string{"none", "auto", "max-content", "min-content"}
+	for _, val := range splitVals {
+		if LengthHandler(val) {
+			continue
+		}
+		valArr := []string{val}
+		if !in(valArr, values) {
+			return false
+		}
+	}
+	return true
+}
+
+func HangingPunctuationHandler(value string) bool {
+	values := []string{"none", "first", "last", "allow-end", "force-end", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func HeightHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func HyphensHandler(value string) bool {
+	values := []string{"none", "manual", "auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ImageRenderingHandler(value string) bool {
+	values := []string{"auto", "smooth", "high-quality", "crisp-edges", "pixelated"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func IsolationHandler(value string) bool {
+	values := []string{"auto", "isolate", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func JustifyContentHandler(value string) bool {
+	values := []string{"flex-start", "flex-end", "center", "space-between", "space-around", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func LetterSpacingHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"normal", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func LineHeightHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"normal", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ListStyleHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		ListStyleTypeHandler,
+		ListStylePositionHandler,
+		ImageHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func ListStylePositionHandler(value string) bool {
+	values := []string{"inside", "outside", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ListStyleTypeHandler(value string) bool {
+	values := []string{"disc", "armenian", "circle", "cjk-ideographic", "decimal", "decimal-leading-zero", "georgian", "hebrew", "hiragana", "hiragana-iroha", "katakana", "katakana-iroha", "lower-alpha", "lower-greek", "lower-latin", "lower-roman", "none", "square", "upper-alpha", "upper-greek", "upper-latin", "upper-roman", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func MarginHandler(value string) bool {
+	values := []string{"auto", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		MarginSideHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func MarginSideHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func MaxHeightWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func MinHeightWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func MixBlendModeHandler(value string) bool {
+	values := []string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "difference", "exclusion", "hue", "saturation", "color", "luminosity"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ObjectFitHandler(value string) bool {
+	values := []string{"fill", "contain", "cover", "none", "scale-down", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ObjectPositionHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 2 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		LengthHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func OpacityHandler(value string) bool {
+	reg := regexp.MustCompile("(0[.]?[0-9]*)|(1.0)")
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OrderHandler(value string) bool {
+	reg := regexp.MustCompile("[0-9]+")
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OutlineHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		ColorHandler,
+		OutlineWidthHandler,
+		OutlineStyleHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func OutlineOffsetHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OutlineStyleHandler(value string) bool {
+	values := []string{"none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OutlineWidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"medium", "thin", "thick", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OverflowHandler(value string) bool {
+	values := []string{"visible", "hidden", "scroll", "auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OverflowXYHandler(value string) bool {
+	values := []string{"visible", "hidden", "scroll", "auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OverflowWrapHandler(value string) bool {
+	values := []string{"normal", "break-word", "anywhere"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func OrphansHandler(value string) bool {
+	reg := regexp.MustCompile(`[0-9]+`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func PaddingHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	if len(splitVals) > 4 {
+		return false
+	}
+	usedFunctions := []func(string) bool{
+		PaddingSideHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func PaddingSideHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func PageBreakBeforeAfterHandler(value string) bool {
+	values := []string{"auto", "always", "avoid", "left", "right", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func PageBreakInsideHandler(value string) bool {
+	values := []string{"auto", "avoid", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func PerspectiveHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func PerspectiveOriginHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	xValues := []string{"left", "center", "right"}
+	yValues := []string{"top", "center", "bottom"}
+	if len(splitVals) > 1 {
+		if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
+			return false
+		}
+		return in([]string{splitVals[1]}, yValues) || LengthHandler(splitVals[1])
+	} else if len(splitVals) == 1 {
+		return in(splitVals, xValues) || in(splitVals, yValues) || LengthHandler(splitVals[0])
+	}
+	return false
+}
+
+func PointerEventsHandler(value string) bool {
+	values := []string{"auto", "none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func PositionHandler(value string) bool {
+	values := []string{"static", "absolute", "fixed", "relative", "sticky", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func QuotesHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func ResizeHandler(value string) bool {
+	values := []string{"none", "both", "horizontal", "vertical", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ScrollBehaviorHandler(value string) bool {
+	values := []string{"auto", "smooth", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TabSizeHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TableLayoutHandler(value string) bool {
+	values := []string{"auto", "fixed", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextAlignHandler(value string) bool {
+	values := []string{"left", "right", "center", "justify", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextAlignLastHandler(value string) bool {
+	values := []string{"auto", "left", "right", "center", "justify", "start", "end", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextCombineUprightHandler(value string) bool {
+	values := []string{"none", "all"}
+	splitVals := splitValues(value)
+	if in(splitVals, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`digits [2-4]`)
+	reg.Longest()
+	return reg.FindString(value) == value && value != ""
+}
+
+func TextDecorationHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		TextDecorationStyleHandler,
+		ColorHandler,
+		TextDecorationLineHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func TextDecorationLineHandler(value string) bool {
+	values := []string{"none", "underline", "overline", "line-through", "initial", "inherit"}
+	splitVals := strings.Split(value, " ")
+	return in(splitVals, values)
+}
+
+func TextDecorationStyleHandler(value string) bool {
+	values := []string{"solid", "double", "dotted", "dashed", "wavy", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextIndentHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextJustifyHandler(value string) bool {
+	values := []string{"auto", "inter-word", "inter-character", "none", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextOverflowHandler(value string) bool {
+	reg := regexp.MustCompile("[\"'][a-z]+[\"']")
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"clip", "ellipsis", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextOrientationHandler(value string) bool {
+	values := []string{"mixed", "upright", "sideways", "sideways-right"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TextShadowHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	commaSplitVals := strings.Split(value, ",")
+	for _, val := range commaSplitVals {
+		splitVals := strings.Split(val, " ")
+		if len(splitVals) > 6 || len(splitVals) < 2 {
+			return false
+		}
+		if !LengthHandler(splitVals[0]) {
+			return false
+		}
+		if !LengthHandler(splitVals[1]) {
+			return false
+		}
+		usedFunctions := []func(string) bool{
+			LengthHandler,
+			ColorHandler,
+		}
+		if len(splitVals) > 2 && !recursiveCheck(splitVals[2:], usedFunctions) {
+			return false
+		}
+	}
+	return true
+}
+
+func TextTransformHandler(value string) bool {
+	values := []string{"none", "capitalize", "uppercase", "lowercase", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TransformHandler(value string) bool {
+	values := []string{"none", "initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	reg := regexp.MustCompile(`matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)`)
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`)
+	reg.Longest()
+	subValue := string(reg.ReplaceAll([]byte(value), []byte{}))
+	trimValue := strings.Split(strings.TrimSuffix(subValue, ")"), ",")
+	valid := true
+	for _, i := range trimValue {
+		if !LengthHandler(strings.TrimSpace(i)) {
+			valid = false
+			break
+		}
+	}
+	if valid && trimValue != nil {
+		return true
+	}
+	reg = regexp.MustCompile(`rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	reg = regexp.MustCompile(`skew(x|y)?\(`)
+	reg.Longest()
+	subValue = string(reg.ReplaceAll([]byte(value), []byte{}))
+	subValue = strings.TrimSuffix(subValue, ")")
+	trimValue = strings.Split(subValue, ",")
+	valid = true
+	for _, i := range trimValue {
+		if !LengthHandler(strings.TrimSpace(i)) {
+			valid = false
+			break
+		}
+	}
+	if valid {
+		return true
+	}
+	reg = regexp.MustCompile(`perspective\(`)
+	reg.Longest()
+	subValue = string(reg.ReplaceAll([]byte(value), []byte{}))
+	subValue = strings.TrimSuffix(subValue, ")")
+	return LengthHandler(subValue)
+}
+
+func TransformOriginHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	xValues := []string{"left", "center", "right"}
+	yValues := []string{"top", "center", "bottom"}
+	if len(splitVals) > 2 {
+		if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
+			return false
+		}
+		if !in([]string{splitVals[1]}, yValues) && !LengthHandler(splitVals[1]) {
+			return false
+		}
+		return LengthHandler(splitVals[2])
+	} else if len(splitVals) > 1 {
+		if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
+			return false
+		}
+		return in([]string{splitVals[1]}, yValues) || LengthHandler(splitVals[1])
+	} else if len(splitVals) == 1 {
+		return in(splitVals, xValues) || in(splitVals, yValues) || LengthHandler(splitVals[0])
+	}
+	return false
+}
+
+func TransformStyleHandler(value string) bool {
+	values := []string{"flat", "preserve-3d", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TransitionHandler(value string) bool {
+	values := []string{"initial", "inherit"}
+	if in([]string{value}, values) {
+		return true
+	}
+	splitVals := strings.Split(value, " ")
+	usedFunctions := []func(string) bool{
+		TransitionPropertyHandler,
+		TransitionDurationHandler,
+		TimingFunctionHandler,
+		TransitionDelayHandler,
+		ColorHandler,
+	}
+	return recursiveCheck(splitVals, usedFunctions)
+}
+
+func TransitionDelayHandler(value string) bool {
+	reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?")
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TransitionDurationHandler(value string) bool {
+	reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?")
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func TransitionPropertyHandler(value string) bool {
+	reg := regexp.MustCompile("([a-zA-Z]+,[ ]?)*[a-zA-Z]+")
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"none", "all", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func UnicodeBidiHandler(value string) bool {
+	values := []string{"normal", "embed", "bidi-override", "isolate", "isolate-override", "plaintext", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func UserSelectHandler(value string) bool {
+	values := []string{"auto", "none", "text", "all"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func VerticalAlignHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func VisiblityHandler(value string) bool {
+	values := []string{"visible", "hidden", "collapse", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func WhiteSpaceHandler(value string) bool {
+	values := []string{"normal", "nowrap", "pre", "pre-line", "pre-wrap", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func WidthHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func WordSpacingHandler(value string) bool {
+	if LengthHandler(value) {
+		return true
+	}
+	values := []string{"normal", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func WordBreakHandler(value string) bool {
+	values := []string{"normal", "break-all", "keep-all", "break-word", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func WordWrapHandler(value string) bool {
+	values := []string{"normal", "break-word", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func WritingModeHandler(value string) bool {
+	values := []string{"horizontal-tb", "vertical-rl", "vertical-lr"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
+
+func ZIndexHandler(value string) bool {
+	reg := regexp.MustCompile(`[\-]?[0-9]+`)
+	reg.Longest()
+	if reg.FindString(value) == value && value != "" {
+		return true
+	}
+	values := []string{"auto", "initial", "inherit"}
+	splitVals := splitValues(value)
+	return in(splitVals, values)
+}
diff --git a/policy.go b/policy.go
index f61d98f..945da07 100644
--- a/policy.go
+++ b/policy.go
@@ -29,6 +29,8 @@
 
 package bluemonday
 
+//TODO sgutzwiller create map of styles to default handlers
+//TODO sgutzwiller create handlers for various attributes
 import (
 	"net/url"
 	"regexp"
@@ -79,6 +81,12 @@ type Policy struct {
 	// map[htmlAttributeName]attrPolicy
 	globalAttrs map[string]attrPolicy
 
+	// map[htmlElementName]map[cssPropertyName]stylePolicy
+	elsAndStyles map[string]map[string]stylePolicy
+
+	// map[cssPropertyName]stylePolicy
+	globalStyles map[string]stylePolicy
+
 	// If urlPolicy is nil, all URLs with matching schema are allowed.
 	// Otherwise, only the URLs with matching schema and urlPolicy(url)
 	// returning true are allowed.
@@ -103,6 +111,20 @@ type attrPolicy struct {
 	regexp *regexp.Regexp
 }
 
+type stylePolicy struct {
+	// handler to validate
+	handler func(string) bool
+
+	// optional pattern to match, when not nil the regexp needs to match
+	// otherwise the property is removed
+	regexp *regexp.Regexp
+
+	// optional list of allowed property values, for properties which
+	// have a defined list of allowed values; property will be removed
+	// if the value is not allowed
+	enum []string
+}
+
 type attrPolicyBuilder struct {
 	p *Policy
 
@@ -111,6 +133,15 @@ type attrPolicyBuilder struct {
 	allowEmpty bool
 }
 
+type stylePolicyBuilder struct {
+	p *Policy
+
+	propertyNames []string
+	regexp        *regexp.Regexp
+	enum          []string
+	handler       func(string) bool
+}
+
 type urlPolicy func(url *url.URL) (allowUrl bool)
 
 // init initializes the maps if this has not been done already
@@ -118,6 +149,8 @@ func (p *Policy) init() {
 	if !p.initialized {
 		p.elsAndAttrs = make(map[string]map[string]attrPolicy)
 		p.globalAttrs = make(map[string]attrPolicy)
+		p.elsAndStyles = make(map[string]map[string]stylePolicy)
+		p.globalStyles = make(map[string]stylePolicy)
 		p.allowURLSchemes = make(map[string]urlPolicy)
 		p.setOfElementsAllowedWithoutAttrs = make(map[string]struct{})
 		p.setOfElementsToSkipContent = make(map[string]struct{})
@@ -265,6 +298,113 @@ func (abp *attrPolicyBuilder) Globally() *Policy {
 	return abp.p
 }
 
+// AllowStyles takes a range of CSS property names and returns a
+// style policy builder that allows you to specify the pattern and scope of
+// the whitelisted property.
+//
+// The style policy is only added to the core policy when either Globally()
+// or OnElements(...) are called.
+func (p *Policy) AllowStyles(propertyNames ...string) *stylePolicyBuilder {
+
+	p.init()
+
+	abp := stylePolicyBuilder{
+		p: p,
+	}
+
+	for _, propertyName := range propertyNames {
+		abp.propertyNames = append(abp.propertyNames, strings.ToLower(propertyName))
+	}
+
+	return &abp
+}
+
+// Matching allows a regular expression to be applied to a nascent style
+// policy, and returns the style policy. Calling this more than once will
+// replace the existing regexp.
+func (spb *stylePolicyBuilder) Matching(regex *regexp.Regexp) *stylePolicyBuilder {
+
+	spb.regexp = regex
+
+	return spb
+}
+
+// MatchingEnum allows a list of allowed values to be applied to a nascent style
+// policy, and returns the style policy. Calling this more than once will
+// replace the existing list of allowed values.
+func (spb *stylePolicyBuilder) MatchingEnum(enum ...string) *stylePolicyBuilder {
+
+	spb.enum = enum
+
+	return spb
+}
+
+// MatchingHandler allows a handler to be applied to a nascent style
+// policy, and returns the style policy. Calling this more than once will
+// replace the existing handler.
+func (spb *stylePolicyBuilder) MatchingHandler(handler func(string) bool) *stylePolicyBuilder {
+
+	spb.handler = handler
+
+	return spb
+}
+
+// OnElements will bind a style policy to a given range of HTML elements
+// and return the updated policy
+func (spb *stylePolicyBuilder) OnElements(elements ...string) *Policy {
+
+	for _, element := range elements {
+		element = strings.ToLower(element)
+
+		for _, attr := range spb.propertyNames {
+
+			if _, ok := spb.p.elsAndStyles[element]; !ok {
+				spb.p.elsAndStyles[element] = make(map[string]stylePolicy)
+			}
+
+			sp := stylePolicy{}
+			if spb.handler != nil {
+				sp.handler = spb.handler
+			} else if len(spb.enum) > 0 {
+				sp.enum = spb.enum
+			} else if spb.regexp != nil {
+				sp.regexp = spb.regexp
+			} else {
+				sp.handler = getDefaultHandler(attr)
+			}
+			spb.p.elsAndStyles[element][attr] = sp
+		}
+	}
+
+	return spb.p
+}
+
+// Globally will bind a style policy to all HTML elements and return the
+// updated policy
+func (spb *stylePolicyBuilder) Globally() *Policy {
+
+	for _, attr := range spb.propertyNames {
+		if _, ok := spb.p.globalStyles[attr]; !ok {
+			spb.p.globalStyles[attr] = stylePolicy{}
+		}
+
+		// Use only one strategy for validating styles, fallback to default
+		sp := stylePolicy{}
+		if spb.handler != nil {
+			sp.handler = spb.handler
+		} else if len(spb.enum) > 0 {
+			sp.enum = spb.enum
+		} else if spb.regexp != nil {
+			sp.regexp = spb.regexp
+		} else {
+			sp.handler = getDefaultHandler(attr)
+		}
+		spb.p.globalStyles[attr] = sp
+	}
+
+	return spb.p
+}
+
 // AllowElements will append HTML elements to the whitelist without applying an
 // attribute policy to those elements (the elements are permitted
 // sans-attributes)
diff --git a/sanitize.go b/sanitize.go
index 2be5ffc..aece8c2 100644
--- a/sanitize.go
+++ b/sanitize.go
@@ -34,15 +34,19 @@ import (
 	"io"
 	"net/url"
 	"regexp"
+	"strconv"
 	"strings"
 
 	"golang.org/x/net/html"
+
+	cssparser "github.com/chris-ramon/douceur/parser"
 )
 
 var (
 	dataAttribute             = regexp.MustCompile("^data-.+")
 	dataAttributeXMLPrefix    = regexp.MustCompile("^xml.+")
 	dataAttributeInvalidChars = regexp.MustCompile("[A-Z;]+")
+	cssUnicodeChar            = regexp.MustCompile(`\\[0-9a-f]{1,6} ?`)
 )
 
 // Sanitize takes a string that contains a HTML fragment or document and applies
@@ -228,7 +232,7 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
 		case html.TextToken:
 
 			if !skipElementContent {
-				switch mostRecentlyStartedToken {
+				switch strings.ToLower(mostRecentlyStartedToken) {
 				case "script":
 					// not encouraged, but if a policy allows JavaScript we
 					// should not HTML escape it as that would break the output
@@ -242,6 +246,7 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
 					buff.WriteString(token.String())
 				}
 			}
+
 		default:
 			// A token that didn't exist in the html package when we wrote this
 			return &bytes.Buffer{}
@@ -262,6 +267,12 @@ func (p *Policy) sanitizeAttrs(
 		return attrs
 	}
 
+	hasStylePolicies := false
+	sps, elementHasStylePolicies := p.elsAndStyles[elementName]
+	if len(p.globalStyles) > 0 || (elementHasStylePolicies && len(sps) > 0) {
+		hasStylePolicies = true
+	}
+
 	// Builds a new attribute slice based on the whether the attribute has been
 	// whitelisted explicitly or globally.
 	cleanAttrs := []html.Attribute{}
@@ -273,6 +284,19 @@ func (p *Policy) sanitizeAttrs(
 				continue
 			}
 		}
+		// Is this a "style" attribute, and if so, do we need to sanitize it?
+		if htmlAttr.Key == "style" && hasStylePolicies {
+			htmlAttr = p.sanitizeStyles(htmlAttr, elementName)
+			if htmlAttr.Val == "" {
+				// We've sanitized away any and all styles; don't bother to
+				// output the style attribute (even if it's allowed)
+				continue
+			} else {
+				cleanAttrs = append(cleanAttrs, htmlAttr)
+				continue
+			}
+		}
+
 		// Is there an element specific attribute policy that applies?
 		if ap, ok := aps[htmlAttr.Key]; ok {
 			if ap.regexp != nil {
@@ -501,6 +525,74 @@ func (p *Policy) sanitizeAttrs(
 	return cleanAttrs
 }
 
+func (p *Policy) sanitizeStyles(attr html.Attribute, elementName string) html.Attribute {
+	sps := p.elsAndStyles[elementName]
+
+	//Add semi-colon to end to fix parsing issue
+	if len(attr.Val) > 0 && attr.Val[len(attr.Val)-1] != ';' {
+		attr.Val = attr.Val + ";"
+	}
+	decs, err := cssparser.ParseDeclarations(attr.Val)
+	if err != nil {
+		attr.Val = ""
+		return attr
+	}
+
+	clean := []string{}
+	prefixes := []string{"-webkit-", "-moz-", "-ms-", "-o-", "mso-", "-xv-", "-atsc-", "-wap-", "-khtml-", "prince-", "-ah-", "-hp-", "-ro-", "-rim-", "-tc-"}
+
+	for _, dec := range decs {
+		addedProperty := false
+		tempProperty := strings.ToLower(dec.Property)
+		tempValue := removeUnicode(strings.ToLower(dec.Value))
+		for _, i := range prefixes {
+			tempProperty = strings.TrimPrefix(tempProperty, i)
+		}
+		if sp, ok := sps[tempProperty]; ok {
+			if sp.handler != nil {
+				if sp.handler(tempValue) {
+					clean = append(clean, dec.Property+": "+dec.Value)
+					addedProperty = true
+				}
+			} else if len(sp.enum) > 0 {
+				if stringInSlice(tempValue, sp.enum) {
+					clean = append(clean, dec.Property+": "+dec.Value)
+					addedProperty = true
+				}
+			} else if sp.regexp != nil {
+				if sp.regexp.MatchString(tempValue) {
+					clean = append(clean, dec.Property+": "+dec.Value)
+					addedProperty = true
+				}
+				continue
+			}
+		}
+
+		if sp, ok := p.globalStyles[tempProperty]; ok && !addedProperty {
+			if sp.handler != nil {
+				if sp.handler(tempValue) {
+					clean = append(clean, dec.Property+": "+dec.Value)
+				}
+			} else if len(sp.enum) > 0 {
+				if stringInSlice(tempValue, sp.enum) {
+					clean = append(clean, dec.Property+": "+dec.Value)
+				}
+			} else if sp.regexp != nil {
+				if sp.regexp.MatchString(tempValue) {
+					clean = append(clean, dec.Property+": "+dec.Value)
+				}
+				continue
+			}
+		}
+	}
+	if len(clean) > 0 {
+		attr.Val = strings.Join(clean, "; ")
+	} else {
+		attr.Val = ""
+	}
+	return attr
+}
+
 func (p *Policy) allowNoAttrs(elementName string) bool {
 	_, ok := p.setOfElementsAllowedWithoutAttrs[elementName]
 	return ok
@@ -530,6 +622,7 @@ func (p *Policy) validURL(rawurl string) (string, bool) {
 			urlPolicy, ok := p.allowURLSchemes[u.Scheme]
 			if !ok {
 				return "", false
+
 			}
 
 			if urlPolicy == nil || urlPolicy(u) == true {
@@ -560,6 +653,16 @@ func linkable(elementName string) bool {
 	}
 }
 
+// stringInSlice returns true if needle exists in haystack
+func stringInSlice(needle string, haystack []string) bool {
+	for _, straw := range haystack {
+		if strings.ToLower(straw) == strings.ToLower(needle) {
+			return true
+		}
+	}
+	return false
+}
+
 func isDataAttribute(val string) bool {
 	if !dataAttribute.MatchString(val) {
 		return false
@@ -578,3 +681,34 @@ func isDataAttribute(val string) bool {
 	}
 	return true
 }
+
+func removeUnicode(value string) string {
+	substitutedValue := value
+	currentLoc := cssUnicodeChar.FindStringIndex(substitutedValue)
+	for currentLoc != nil {
+
+		character := substitutedValue[currentLoc[0]+1 : currentLoc[1]]
+		character = strings.TrimSpace(character)
+		if len(character) < 4 {
+			character = strings.Repeat("0", 4-len(character)) + character
+		} else {
+			for len(character) > 4 {
+				if character[0] != '0' {
+					character = ""
+					break
+				} else {
+					character = character[1:]
+				}
+			}
+		}
+		character = "\\u" + character
+		translatedChar, err := strconv.Unquote(`"` + character + `"`)
+		translatedChar = strings.TrimSpace(translatedChar)
+		if err != nil {
+			return ""
+		}
+		substitutedValue = substitutedValue[0:currentLoc[0]] + translatedChar + substitutedValue[currentLoc[1]:]
+		currentLoc = cssUnicodeChar.FindStringIndex(substitutedValue)
+	}
+	return substitutedValue
+}


Test patch:
diff --git a/example_test.go b/example_test.go
index e85f5dd..fc279df 100644
--- a/example_test.go
+++ b/example_test.go
@@ -191,6 +191,45 @@ func ExamplePolicy_AllowAttrs() {
 	).OnElements("td", "th")
 }
 
+func ExamplePolicy_AllowStyles() {
+	p := bluemonday.NewPolicy()
+
+	// Allow only 'span' and 'p' elements
+	p.AllowElements("span", "p", "strong")
+
+	// Only allow 'style' attributes on 'span' and 'p' elements
+	p.AllowAttrs("style").OnElements("span", "p")
+
+	// Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none'
+	// on 'span' elements only
+	p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElements("span")
+
+	// Allow the 'color' property with valid RGB(A) hex values only
+	// on every HTML element that has been whitelisted
+	p.AllowStyles("color").Matching(regexp.MustCompile("(?i)^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$")).Globally()
+
+	// Default handler
+	p.AllowStyles("background-origin").Globally()
+
+	// The span has an invalid 'color' which will be stripped along with other disallowed properties
+	html := p.Sanitize(
+		`<p style="color:#f00;">
+	<span style="text-decoration: underline; background-image: url(javascript:alert('XSS')); color: #f00ba; background-origin: invalidValue">
+		Red underlined <strong style="text-decoration:none;">text</strong>
+	</span>
+</p>`,
+	)
+
+	fmt.Println(html)
+
+	// Output:
+	//<p style="color: #f00">
+	//	<span style="text-decoration: underline">
+	//		Red underlined <strong>text</strong>
+	//	</span>
+	//</p>
+}
+
 func ExamplePolicy_AllowElements() {
 	p := bluemonday.NewPolicy()
 
diff --git a/handlers_test.go b/handlers_test.go
new file mode 100644
index 0000000..50acf6e
--- /dev/null
+++ b/handlers_test.go
@@ -0,0 +1,1284 @@
+// Copyright (c) 2019, David Kitchen <david@buro9.com></div>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+//   list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+//
+// * Neither the name of the organisation (Microcosm) nor the names of its
+//   contributors may be used to endorse or promote products derived from
+//   this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package bluemonday
+
+import (
+	"sync"
+	"testing"
+)
+
+func TestDefaultHandlers(t *testing.T) {
+
+	tests := []test{
+		{
+			in:       `<div style="nonexistentStyle: something;"></div>`,
+			expected: `<div></div>`,
+		},
+		{
+			in:       `<div style="aLiGn-cOntEnt: cEntEr;"></div>`,
+			expected: `<div style="aLiGn-cOntEnt: cEntEr"></div>`,
+		},
+		{
+			in:       `<div style="align-items: center;"></div>`,
+			expected: `<div style="align-items: center"></div>`,
+		},
+		{
+			in:       `<div style="align-self: center;"></div>`,
+			expected: `<div style="align-self: center"></div>`,
+		},
+		{
+			in:       `<div style="all: initial;"></div>`,
+			expected: `<div style="all: initial"></div>`,
+		},
+		{
+			in: `<div style="animation: mymove 5s infinite;"></div><div` +
+				` style="animation: inherit;"></div>`,
+			expected: `<div style="animation: mymove 5s infinite"></div><div` +
+				` style="animation: inherit"></div>`,
+		},
+		{
+			in: `<div style="animation-delay: 2s;"></div><div style=` +
+				`"animation-delay: initial;"></div>`,
+			expected: `<div style="animation-delay: 2s"></div><div style=` +
+				`"animation-delay: initial"></div>`,
+		},
+		{
+			in:       `<div style="animation-direction: alternate;"></div>`,
+			expected: `<div style="animation-direction: alternate"></div>`,
+		},
+		{
+			in: `<div style="animation-duration: 2s;"></div><div style=` +
+				`"animation-duration: initial;"></div>`,
+			expected: `<div style="animation-duration: 2s"></div><div style=` +
+				`"animation-duration: initial"></div>`,
+		},
+		{
+			in:       `<div style="animation-fill-mode: forwards;"></div>`,
+			expected: `<div style="animation-fill-mode: forwards"></div>`,
+		},
+		{
+			in: `<div style="animation-iteration-count: 4;"></div><div ` +
+				`style="animation-iteration-count: inherit;"></div>`,
+			expected: `<div style="animation-iteration-count: 4"></div><div ` +
+				`style="animation-iteration-count: inherit"></div>`,
+		},
+		{
+			in: `<div style="animation-name: chuck;"></div><div style=` +
+				`"animation-name: none"></div>`,
+			expected: `<div style="animation-name: chuck"></div><div style=` +
+				`"animation-name: none"></div>`,
+		},
+		{
+			in:       `<div style="animation-play-state: running;"></div>`,
+			expected: `<div style="animation-play-state: running"></div>`,
+		},
+		{
+			in: `<div style="animation-timing-function: ` +
+				`cubic-bezier(1,1,1,1);"></div><div style=` +
+				`"animation-timing-function: steps(2, start);"></div>`,
+			expected: `<div style="animation-timing-function: ` +
+				`cubic-bezier(1,1,1,1)"></div><div style=` +
+				`"animation-timing-function: steps(2, start)"></div>`,
+		},
+		{
+			in:       `<div style="backface-visibility: hidden"></div>`,
+			expected: `<div style="backface-visibility: hidden"></div>`,
+		},
+		{
+			in: `<div style="background: lightblue ` +
+				`url('https://img_tree.gif') no-repeat fixed center"></div>` +
+				`<div style="background: initial"></div>`,
+			expected: `<div style="background: lightblue ` +
+				`url(&#39;https://img_tree.gif&#39;) no-repeat fixed center">` +
+				`</div><div style="background: initial"></div>`,
+		},
+		{
+			in:       `<div style="background-attachment: fixed"></div>`,
+			expected: `<div style="background-attachment: fixed"></div>`,
+		},
+		{
+			in:       `<div style="background-blend-mode: lighten"></div>`,
+			expected: `<div style="background-blend-mode: lighten"></div>`,
+		},
+		{
+			in:       `<div style="background-clip: padding-box"></div>`,
+			expected: `<div style="background-clip: padding-box"></div>`,
+		},
+		{
+			in:       `<div style="background-color: coral"></div>`,
+			expected: `<div style="background-color: coral"></div>`,
+		},
+		{
+			in: `<div style="background-image: url('http://paper.gif')">` +
+				`</div><div style="background-image: inherit"></div>`,
+			expected: `<div style="background-image: ` +
+				`url(&#39;http://paper.gif&#39;)"></div><div style="` +
+				`background-image: inherit"></div>`,
+		},
+		{
+			in:       `<div style="background-origin: content-box"></div>`,
+			expected: `<div style="background-origin: content-box"></div>`,
+		},
+		{
+			in: `<div style="background-position: center"></div><div ` +
+				`style="background-position: 20px 20px"></div>`,
+			expected: `<div style="background-position: center"></div><div ` +
+				`style="background-position: 20px 20px"></div>`,
+		},
+		{
+			in:       `<div style="background-repeat: repeat-y"></div>`,
+			expected: `<div style="background-repeat: repeat-y"></div>`,
+		},
+		{
+			in: `<div style="background-size: 300px 100px"></div><div ` +
+				`style="background-size: initial"></div>`,
+			expected: `<div style="background-size: 300px 100px"></div>` +
+				`<div style="background-size: initial"></div>`,
+		},
+		{
+			in: `<div style="border: 4px dotted blue;"></div><div ` +
+				`style="border: initial;"></div>`,
+			expected: `<div style="border: 4px dotted blue"></div><div ` +
+				`style="border: initial"></div>`,
+		},
+		{
+			in: `<div style="border-bottom: 4px dotted blue;"></div>` +
+				`<div style="border-bottom: initial"></div>`,
+			expected: `<div style="border-bottom: 4px dotted blue"></div>` +
+				`<div style="border-bottom: initial"></div>`,
+		},
+		{
+			in:       `<div style="border-bottom-color: blue;"></div>`,
+			expected: `<div style="border-bottom-color: blue"></div>`,
+		},
+		{
+			in: `<div style="border-bottom-left-radius: 4px;"></div>` +
+				`<div style="border-bottom-left-radius: initial"></div>`,
+			expected: `<div style="border-bottom-left-radius: 4px"></div>` +
+				`<div style="border-bottom-left-radius: initial"></div>`,
+		},
+		{
+			in: `<div style="border-bottom-right-radius: 40px 4px;">` +
+				`</div>`,
+			expected: `<div style="border-bottom-right-radius: 40px 4px">` +
+				`</div>`,
+		},
+		{
+			in:       `<div style="border-bottom-style: dotted;"></div>`,
+			expected: `<div style="border-bottom-style: dotted"></div>`,
+		},
+		{
+			in:       `<div style="border-bottom-width: thin;"></div>`,
+			expected: `<div style="border-bottom-width: thin"></div>`,
+		},
+		{
+			in:       `<div style="border-collapse: separate;"></div>`,
+			expected: `<div style="border-collapse: separate"></div>`,
+		},
+		{
+			in:       `<div style="border-color: coral;"></div>`,
+			expected: `<div style="border-color: coral"></div>`,
+		},
+		{
+			in: `<div style="border-image: url(https://border.png) 30 ` +
+				`round;"></div><div style="border-image: initial;"></div>`,
+			expected: `<div style="border-image: url(https://border.png) 30 ` +
+				`round"></div><div style="border-image: initial"></div>`,
+		},
+		{
+			in:       `<div style="border-image-outset: 10px;"></div>`,
+			expected: `<div style="border-image-outset: 10px"></div>`,
+		},
+		{
+			in:       `<div style="border-image-repeat: repeat;"></div>`,
+			expected: `<div style="border-image-repeat: repeat"></div>`,
+		},
+		{
+			in: `<div style="border-image-slice: 30%;"></div><div ` +
+				`style="border-image-slice: fill;"></div><div style="` +
+				`border-image-slice: 3% 3% 3% 3% 3%;"></div>`,
+			expected: `<div style="border-image-slice: 30%"></div><div style` +
+				`="border-image-slice: fill"></div><div></div>`,
+		},
+		{
+			in: `<div style="border-image-source: ` +
+				`url(https://border.png);"></div>`,
+			expected: `<div style="border-image-source: ` +
+				`url(https://border.png)"></div>`,
+		},
+		{
+			in:       `<div style="border-image-width: 10px;"></div>`,
+			expected: `<div style="border-image-width: 10px"></div>`,
+		},
+		{
+			in:       `<div style="border-left: 4px dotted blue;"></div>`,
+			expected: `<div style="border-left: 4px dotted blue"></div>`,
+		},
+		{
+			in:       `<div style="border-left-color: blue;"></div>`,
+			expected: `<div style="border-left-color: blue"></div>`,
+		},
+		{
+			in:       `<div style="border-left-style: dotted;"></div>`,
+			expected: `<div style="border-left-style: dotted"></div>`,
+		},
+		{
+			in:       `<div style="border-left-width: thin;"></div>`,
+			expected: `<div style="border-left-width: thin"></div>`,
+		},
+		{
+			in: `<div style="border-radius: 25px;"></div><div style=` +
+				`"border-radius: initial;"></div><div style="border-radius:` +
+				` 1px 1px 1px 1px 1px;"></div>`,
+			expected: `<div style="border-radius: 25px"></div><div style=` +
+				`"border-radius: initial"></div><div></div>`,
+		},
+		{
+			in:       `<div style="border-left: 4px dotted blue;"></div>`,
+			expected: `<div style="border-left: 4px dotted blue"></div>`,
+		},
+		{
+			in:       `<div style="border-right-color: blue;"></div>`,
+			expected: `<div style="border-right-color: blue"></div>`,
+		},
+		{
+			in:       `<div style="border-right-style: dotted;"></div>`,
+			expected: `<div style="border-right-style: dotted"></div>`,
+		},
+		{
+			in:       `<div style="border-right-width: thin;"></div>`,
+			expected: `<div style="border-right-width: thin"></div>`,
+		},
+		{
+			in:       `<div style="border-spacing: 15px;"></div>`,
+			expected: `<div style="border-spacing: 15px"></div>`,
+		},
+		{
+			in: `<div style="border-style: dotted;"></div><div style="` +
+				`border-style: initial;"></div><div style="border-style: ` +
+				`dotted dotted dotted dotted dotted;"></div>`,
+			expected: `<div style="border-style: dotted"></div><div style=` +
+				`"border-style: initial"></div><div></div>`,
+		},
+		{
+			in:       `<div style="border-top: 4px dotted blue;"></div>`,
+			expected: `<div style="border-top: 4px dotted blue"></div>`,
+		},
+		{
+			in:       `<div style="border-top-color: blue;"></div>`,
+			expected: `<div style="border-top-color: blue"></div>`,
+		},
+		{
+			in:       `<div style="border-top-left-radius: 4px;"></div>`,
+			expected: `<div style="border-top-left-radius: 4px"></div>`,
+		},
+		{
+			in:       `<div style="border-top-right-radius: 40px 4px;"></div>`,
+			expected: `<div style="border-top-right-radius: 40px 4px"></div>`,
+		},
+		{
+			in:       `<div style="border-top-style: dotted;"></div>`,
+			expected: `<div style="border-top-style: dotted"></div>`,
+		},
+		{
+			in:       `<div style="border-top-width: thin;"></div>`,
+			expected: `<div style="border-top-width: thin"></div>`,
+		},
+		{
+			in: `<div style="border-width: thin;"></div><div style="` +
+				`border-width: initial;"></div><div style="border-width: ` +
+				`thin thin thin thin thin;"></div>`,
+			expected: `<div style="border-width: thin"></div><div style="` +
+				`border-width: initial"></div><div></div>`,
+		},
+		{
+			in: `<div style="bottom: 10px;"></div><div style="bottom:` +
+				` auto;"></div>`,
+			expected: `<div style="bottom: 10px"></div><div style="bottom:` +
+				` auto"></div>`,
+		},
+		{
+			in:       `<div style="box-decoration-break: slice;"></div>`,
+			expected: `<div style="box-decoration-break: slice"></div>`,
+		},
+		{
+			in: `<div style="box-shadow: 10px 10px #888888;"></div>` +
+				`<div style="box-shadow: aa;"></div><div style="box-shadow: ` +
+				`10px aa;"></div><div style="box-shadow: 10px;"></div><div ` +
+				`style="box-shadow: 10px 10px aa;"></div>`,
+			expected: `<div style="box-shadow: 10px 10px #888888"></div>` +
+				`<div></div><div></div><div></div><div></div>`,
+		},
+		{
+			in:       `<div style="box-sizing: border-box;"></div>`,
+			expected: `<div style="box-sizing: border-box"></div>`,
+		},
+		{
+			in:       `<div style="break-after: column;"></div>`,
+			expected: `<div style="break-after: column"></div>`,
+		},
+		{
+			in:       `<div style="break-before: column;"></div>`,
+			expected: `<div style="break-before: column"></div>`,
+		},
+		{
+			in:       `<div style="break-inside: avoid-column;"></div>`,
+			expected: `<div style="break-inside: avoid-column"></div>`,
+		},
+		{
+			in:       `<div style="caption-side: bottom;"></div>`,
+			expected: `<div style="caption-side: bottom"></div>`,
+		},
+		{
+			in: `<div style="caret-color: red;"></div><div style=` +
+				`"caret-color: rgb(2,2,2);"></div><div style="caret-color:` +
+				` rgba(2,2,2,0.5);"></div><div style="caret-color: ` +
+				`hsl(2,2%,2%);"></div><div style="caret-color: ` +
+				`hsla(2,2%,2%,0.5);"></div>`,
+			expected: `<div style="caret-color: red"></div><div style=` +
+				`"caret-color: rgb(2,2,2)"></div><div style="caret-color: ` +
+				`rgba(2,2,2,0.5)"></div><div style="caret-color: ` +
+				`hsl(2,2%,2%)"></div><div style="caret-color: ` +
+				`hsla(2,2%,2%,0.5)"></div>`,
+		},
+		{
+			in:       `<div style="clear: both;"></div>`,
+			expected: `<div style="clear: both"></div>`,
+		},
+		{
+			in: `<div style="clip: rect(0px,60px,200px,0px);"></div>` +
+				`<div style="clip: auto;"></div>`,
+			expected: `<div style="clip: rect(0px,60px,200px,0px)"></div>` +
+				`<div style="clip: auto"></div>`,
+		},
+		{
+			in: `<div style="color: red;"></div><div style="color: ` +
+				`rgb(2,2,2);"></div><div style="color: rgba(2,2,2,0.5);">` +
+				`</div><div style="color: hsl(2,2%,2%);"></div><div style="` +
+				`color: hsla(2,2%,2%,0.5);"></div>`,
+			expected: `<div style="color: red"></div><div style="color: ` +
+				`rgb(2,2,2)"></div><div style="color: rgba(2,2,2,0.5)">` +
+				`</div><div style="color: hsl(2,2%,2%)"></div><div style="` +
+				`color: hsla(2,2%,2%,0.5)"></div>`,
+		},
+		{
+			in:       `<div style="clear: both;"></div>`,
+			expected: `<div style="clear: both"></div>`,
+		},
+		{
+			in: `<div style="column-count: 3;"></div><div style="` +
+				`column-count: auto;"></div>`,
+			expected: `<div style="column-count: 3"></div><div style="` +
+				`column-count: auto"></div>`,
+		},
+		{
+			in:       `<div style="column-fill: balance;"></div>`,
+			expected: `<div style="column-fill: balance"></div>`,
+		},
+		{
+			in: `<div style="column-gap: 40px;"></div><div style="` +
+				`column-gap: normal;"></div>`,
+			expected: `<div style="column-gap: 40px"></div><div style="` +
+				`column-gap: normal"></div>`,
+		},
+		{
+			in:       `<div style="column-rule: 4px double #ff00ff;"></div>`,
+			expected: `<div style="column-rule: 4px double #ff00ff"></div>`,
+		},
+		{
+			in:       `<div style="column-rule-color: #ff00ff;"></div>`,
+			expected: `<div style="column-rule-color: #ff00ff"></div>`,
+		},
+		{
+			in:       `<div style="column-rule: red;"></div>`,
+			expected: `<div style="column-rule: red"></div>`,
+		},
+		{
+			in:       `<div style="column-rule-width: 4px;"></div>`,
+			expected: `<div style="column-rule-width: 4px"></div>`,
+		},
+		{
+			in:       `<div style="column-span: all;"></div>`,
+			expected: `<div style="column-span: all"></div>`,
+		},
+		{
+			in: `<div style="column-width: 4px;"></div><div style="` +
+				`column-width: auto;"></div>`,
+			expected: `<div style="column-width: 4px"></div><div style="` +
+				`column-width: auto"></div>`,
+		},
+		{
+			in: `<div style="columns: 4px 3"></div><div style="` +
+				`columns: auto"></div>`,
+			expected: `<div style="columns: 4px 3"></div><div style="` +
+				`columns: auto"></div>`,
+		},
+		{
+			in:       `<div style="cursor: alias"></div>`,
+			expected: `<div style="cursor: alias"></div>`,
+		},
+		{
+			in:       `<div style="direction: rtl"></div>`,
+			expected: `<div style="direction: rtl"></div>`,
+		},
+		{
+			in:       `<div style="display: block"></div>`,
+			expected: `<div style="display: block"></div>`,
+		},
+		{
+			in:       `<div style="empty-cells: hide"></div>`,
+			expected: `<div style="empty-cells: hide"></div>`,
+		},
+		{
+			in: `<div style="filter: grayscale(100%)"></div><div style` +
+				`="filter: sepia(100%)"></div>`,
+			expected: `<div style="filter: grayscale(100%)"></div><div style` +
+				`="filter: sepia(100%)"></div>`,
+		},
+		{
+			in: `<div style="flex: 1"></div><div style="flex: auto">` +
+				`</div>`,
+			expected: `<div style="flex: 1"></div><div style="flex: auto">` +
+				`</div>`,
+		},
+		{
+			in: `<div style="flex-basis: 10px"></div><div style="` +
+				`flex-basis: auto"></div>`,
+			expected: `<div style="flex-basis: 10px"></div><div style="` +
+				`flex-basis: auto"></div>`,
+		},
+		{
+			in:       `<div style="flex-direction: row-reverse"></div>`,
+			expected: `<div style="flex-direction: row-reverse"></div>`,
+		},
+		{
+			in: `<div style="flex-flow: row-reverse wrap"></div><div ` +
+				`style="flex-flow: initial"></div>`,
+			expected: `<div style="flex-flow: row-reverse wrap"></div><div ` +
+				`style="flex-flow: initial"></div>`,
+		},
+		{
+			in: `<div style="flex-grow: 1"></div><div style="flex-grow` +
+				`: initial"></div>`,
+			expected: `<div style="flex-grow: 1"></div><div style="flex-grow` +
+				`: initial"></div>`,
+		},
+		{
+			in:       `<div style="flex-shrink: 3"></div>`,
+			expected: `<div style="flex-shrink: 3"></div>`,
+		},
+		{
+			in:       `<div style="flex-wrap: wrap"></div>`,
+			expected: `<div style="flex-wrap: wrap"></div>`,
+		},
+		{
+			in:       `<div style="float: right"></div>`,
+			expected: `<div style="float: right"></div>`,
+		},
+		{
+			in: `<div style="font: italic bold 12px/30px Georgia, serif` +
+				`"></div><div style="font: icon"></div>`,
+			expected: `<div style="font: italic bold 12px/30px Georgia, serif` +
+				`"></div><div style="font: icon"></div>`,
+		},
+		{
+			in: `<div style="font-family: 'Times New Roman', Times, ` +
+				`serif"></div>`,
+			expected: `<div style="font-family: &#39;Times New Roman&#39;,` +
+				` Times, serif"></div>`,
+		},
+		{
+			in:       `<div style="font-kerning: normal"></div>`,
+			expected: `<div style="font-kerning: normal"></div>`,
+		},
+		{
+			in:       `<div style="font-language-override: normal"></div>`,
+			expected: `<div style="font-language-override: normal"></div>`,
+		},
+		{
+			in:       `<div style="font-size: large"></div>`,
+			expected: `<div style="font-size: large"></div>`,
+		},
+		{
+			in: `<div style="font-size-adjust: 0.58"></div><div style="` +
+				`font-size-adjust: auto"></div>`,
+			expected: `<div style="font-size-adjust: 0.58"></div><div style="` +
+				`font-size-adjust: auto"></div>`,
+		},
+		{
+			in:       `<div style="font-stretch: expanded"></div>`,
+			expected: `<div style="font-stretch: expanded"></div>`,
+		},
+		{
+			in:       `<div style="font-style: italic"></div>`,
+			expected: `<div style="font-style: italic"></div>`,
+		},
+		{
+			in:       `<div style="font-synthesis: style"></div>`,
+			expected: `<div style="font-synthesis: style"></div>`,
+		},
+		{
+			in:       `<div style="font-variant: small-caps"></div>`,
+			expected: `<div style="font-variant: small-caps"></div>`,
+		},
+		{
+			in:       `<div style="font-variant-caps: small-caps"></div>`,
+			expected: `<div style="font-variant-caps: small-caps"></div>`,
+		},
+		{
+			in:       `<div style="font-variant-position: sub"></div>`,
+			expected: `<div style="font-variant-position: sub"></div>`,
+		},
+		{
+			in:       `<div style="font-weight: normal"></div>`,
+			expected: `<div style="font-weight: normal"></div>`,
+		},
+		{
+			in: `<div style="grid: 150px / auto auto auto;"></div><div ` +
+				`style="grid: none;"></div>`,
+			expected: `<div style="grid: 150px / auto auto auto"></div><div ` +
+				`style="grid: none"></div>`,
+		},
+		{
+			in: `<div style="grid-area: 2 / 1 / span 2 / span 3;">` +
+				`</div>`,
+			expected: `<div style="grid-area: 2 / 1 / span 2 / span 3">` +
+				`</div>`,
+		},
+		{
+			in: `<div style="grid-auto-columns: 150px;"></div>` +
+				`<div style="grid-auto-columns: auto;"></div>`,
+			expected: `<div style="grid-auto-columns: 150px"></div>` +
+				`<div style="grid-auto-columns: auto"></div>`,
+		},
+		{
+			in:       `<div style="grid-auto-flow: column;"></div>`,
+			expected: `<div style="grid-auto-flow: column"></div>`,
+		},
+		{
+			in:       `<div style="grid-auto-rows: 150px;"></div>`,
+			expected: `<div style="grid-auto-rows: 150px"></div>`,
+		},
+		{
+			in:       `<div style="grid-column: 1 / span 2;"></div>`,
+			expected: `<div style="grid-column: 1 / span 2"></div>`,
+		},
+		{
+			in: `<div style="grid-column-end: span 2;"></div>` +
+				`<div style="grid-column-end: auto;"></div>`,
+			expected: `<div style="grid-column-end: span 2"></div>` +
+				`<div style="grid-column-end: auto"></div>`,
+		},
+		{
+			in:       `<div style="grid-column-gap: 10px;"></div>`,
+			expected: `<div style="grid-column-gap: 10px"></div>`,
+		},
+		{
+			in:       `<div style="grid-column-start: 1;"></div>`,
+			expected: `<div style="grid-column-start: 1"></div>`,
+		},
+		{
+			in: `<div style="grid-gap: 1px;"></div><div style=` +
+				`"grid-gap: 1px 1px 1px;"></div>`,
+			expected: `<div style="grid-gap: 1px"></div><div></div>`,
+		},
+		{
+			in:       `<div style="grid-row: 1 / span 2;"></div>`,
+			expected: `<div style="grid-row: 1 / span 2"></div>`,
+		},
+		{
+			in:       `<div style="grid-row-end: span 2;"></div>`,
+			expected: `<div style="grid-row-end: span 2"></div>`,
+		},
+		{
+			in:       `<div style="grid-row-gap: 10px;"></div>`,
+			expected: `<div style="grid-row-gap: 10px"></div>`,
+		},
+		{
+			in:       `<div style="grid-row-start: 1;"></div>`,
+			expected: `<div style="grid-row-start: 1"></div>`,
+		},
+		{
+			in: `<div style="grid-template: 150px / auto auto auto;">` +
+				`</div><div style="grid-template: none"></div><div style="` +
+				`grid-template: a / a / a"></div>`,
+			expected: `<div style="grid-template: 150px / auto auto auto">` +
+				`</div><div style="grid-template: none"></div><div></div>`,
+		},
+		{
+			in: `<div style="grid-template-areas: none;"></div><div ` +
+				`style="grid-template-areas: 'Billy'"></div>`,
+			expected: `<div style="grid-template-areas: none"></div>` +
+				`<div style="grid-template-areas: &#39;Billy&#39;"></div>`,
+		},
+		{
+			in: `<div style="grid-template-columns: auto auto auto` +
+				` auto auto;"></div>`,
+			expected: `<div style="grid-template-columns: auto auto` +
+				` auto auto auto"></div>`,
+		},
+		{
+			in: `<div style="grid-template-rows: 150px 150px">` +
+				`</div><div style="grid-template-rows: aaaa aaaaa"></div>`,
+			expected: `<div style="grid-template-rows: 150px 150px">` +
+				`</div><div></div>`,
+		},
+		{
+			in:       `<div style="hanging-punctuation: first;"></div>`,
+			expected: `<div style="hanging-punctuation: first"></div>`,
+		},
+		{
+			in: `<div style="height: 50px;"></div><div style="height: ` +
+				`auto;"></div>`,
+			expected: `<div style="height: 50px"></div><div style="height: ` +
+				`auto"></div>`,
+		},
+		{
+			in:       `<div style="hyphens: manual;"></div>`,
+			expected: `<div style="hyphens: manual"></div>`,
+		},
+		{
+			in:       `<div style="isolation: isolate;"></div>`,
+			expected: `<div style="isolation: isolate"></div>`,
+		},
+		{
+			in:       `<div style="image-rendering: smooth;"></div>`,
+			expected: `<div style="image-rendering: smooth"></div>`,
+		},
+		{
+			in:       `<div style="justify-content: center;"></div>`,
+			expected: `<div style="justify-content: center"></div>`,
+		},
+		{
+			in:       `<div style="left: 150px;"></div>`,
+			expected: `<div style="left: 150px"></div>`,
+		},
+		{
+			in: `<div style="letter-spacing: -3px;"></div><div style` +
+				`="letter-spacing: normal;"></div>`,
+			expected: `<div style="letter-spacing: -3px"></div><div style` +
+				`="letter-spacing: normal"></div>`,
+		},
+		{
+			in:       `<div style="line-break: auto"></div>`,
+			expected: `<div style="line-break: auto"></div>`,
+		},
+		{
+			in: `<div style="line-height: 1.6;"></div><div style=` +
+				`"line-height: normal;"></div>`,
+			expected: `<div style="line-height: 1.6"></div><div style=` +
+				`"line-height: normal"></div>`,
+		},
+		{
+			in: `<div style="list-style: square inside ` +
+				`url(http://sqpurple.gif);"></div><div style="list-style: ` +
+				`initial"></div>`,
+			expected: `<div style="list-style: square inside ` +
+				`url(http://sqpurple.gif)"></div><div style="list-style: ` +
+				`initial"></div>`,
+		},
+		{
+			in: `<div style="list-style-image: ` +
+				`url(http://sqpurple.gif);"></div>`,
+			expected: `<div style="list-style-image: ` +
+				`url(http://sqpurple.gif)"></div>`,
+		},
+		{
+			in:       `<div style="list-style-position: inside;"></div>`,
+			expected: `<div style="list-style-position: inside"></div>`,
+		},
+		{
+			in:       `<div style="list-style-type: square;"></div>`,
+			expected: `<div style="list-style-type: square"></div>`,
+		},
+		{
+			in: `<div style="margin: 150px;"></div><div style="margin:` +
+				` auto;"></div>`,
+			expected: `<div style="margin: 150px"></div><div style="margin:` +
+				` auto"></div>`,
+		},
+		{
+			in: `<div style="margin-bottom: 150px;"></div><div ` +
+				`style="margin-bottom: auto;"></div>`,
+			expected: `<div style="margin-bottom: 150px"></div><div ` +
+				`style="margin-bottom: auto"></div>`,
+		},
+		{
+			in:       `<div style="margin-left: 150px;"></div>`,
+			expected: `<div style="margin-left: 150px"></div>`,
+		},
+		{
+			in:       `<div style="margin-right: 150px;"></div>`,
+			expected: `<div style="margin-right: 150px"></div>`,
+		},
+		{
+			in:       `<div style="margin-top: 150px;"></div>`,
+			expected: `<div style="margin-top: 150px"></div>`,
+		},
+		{
+			in: `<div style="max-height: 150px;"></div><div style=` +
+				`"max-height: initial;"></div>`,
+			expected: `<div style="max-height: 150px"></div><div style=` +
+				`"max-height: initial"></div>`,
+		},
+		{
+			in:       `<div style="max-width: 150px;"></div>`,
+			expected: `<div style="max-width: 150px"></div>`,
+		},
+		{
+			in: `<div style="min-height: 150px;"></div><div style=` +
+				`"min-height: initial;"></div>`,
+			expected: `<div style="min-height: 150px"></div><div style=` +
+				`"min-height: initial"></div>`,
+		},
+		{
+			in:       `<div style="min-width: 150px;"></div>`,
+			expected: `<div style="min-width: 150px"></div>`,
+		},
+		{
+			in:       `<div style="mix-blend-mode: darken;"></div>`,
+			expected: `<div style="mix-blend-mode: darken"></div>`,
+		},
+		{
+			in:       `<div style="object-fit: cover;"></div>`,
+			expected: `<div style="object-fit: cover"></div>`,
+		},
+		{
+			in: `<div style="object-position: 5px 10%;"></div><div ` +
+				`style="object-position: initial"></div><div style="` +
+				`object-position: 5px 10% 5px;"></div>`,
+			expected: `<div style="object-position: 5px 10%"></div><div ` +
+				`style="object-position: initial"></div><div></div>`,
+		},
+		{
+			in: `<div style="opacity: 0.5;"></div><div style="opacity:` +
+				` initial"></div>`,
+			expected: `<div style="opacity: 0.5"></div><div style="opacity:` +
+				` initial"></div>`,
+		},
+		{
+			in: `<div style="order: 2;"></div><div style="order: ` +
+				`initial"></div>`,
+			expected: `<div style="order: 2"></div><div style="order: ` +
+				`initial"></div>`,
+		},
+		{
+			in: `<div style="outline: 2px dashed blue;"></div><div ` +
+				`style="outline: initial"></div>`,
+			expected: `<div style="outline: 2px dashed blue"></div><div ` +
+				`style="outline: initial"></div>`,
+		},
+		{
+			in:       `<div style="outline-color: blue;"></div>`,
+			expected: `<div style="outline-color: blue"></div>`,
+		},
+		{
+			in: `<div style="outline-offset: 2px;"></div><div ` +
+				`style="outline-offset: initial;"></div>`,
+			expected: `<div style="outline-offset: 2px"></div><div ` +
+				`style="outline-offset: initial"></div>`,
+		},
+		{
+			in:       `<div style="outline-style: dashed;"></div>`,
+			expected: `<div style="outline-style: dashed"></div>`,
+		},
+		{
+			in:       `<div style="outline-width: thick;"></div>`,
+			expected: `<div style="outline-width: thick"></div>`,
+		},
+		{
+			in:       `<div style="overflow: scroll;"></div>`,
+			expected: `<div style="overflow: scroll"></div>`,
+		},
+		{
+			in:       `<div style="overflow-x: scroll;"></div>`,
+			expected: `<div style="overflow-x: scroll"></div>`,
+		},
+		{
+			in:       `<div style="overflow-y: scroll;"></div>`,
+			expected: `<div style="overflow-y: scroll"></div>`,
+		},
+		{
+			in:       `<div style="overflow-wrap: anywhere;"></div>`,
+			expected: `<div style="overflow-wrap: anywhere"></div>`,
+		},
+		{
+			in:       `<div style="orphans: 2;"></div>`,
+			expected: `<div style="orphans: 2"></div>`,
+		},
+		{
+			in:       `<div style="padding: 55px;"></div>`,
+			expected: `<div style="padding: 55px"></div>`,
+		},
+		{
+			in: `<div style="padding-bottom: 55px;"></div><div style` +
+				`="padding-bottom: initial;"></div>`,
+			expected: `<div style="padding-bottom: 55px"></div><div style=` +
+				`"padding-bottom: initial"></div>`,
+		},
+		{
+			in:       `<div style="padding-left: 55px;"></div>`,
+			expected: `<div style="padding-left: 55px"></div>`,
+		},
+		{
+			in:       `<div style="padding-right: 55px;"></div>`,
+			expected: `<div style="padding-right: 55px"></div>`,
+		},
+		{
+			in:       `<div style="padding-top: 55px;"></div>`,
+			expected: `<div style="padding-top: 55px"></div>`,
+		},
+		{
+			in:       `<div style="page-break-after: always;"></div>`,
+			expected: `<div style="page-break-after: always"></div>`,
+		},
+		{
+			in:       `<div style="page-break-before: always;"></div>`,
+			expected: `<div style="page-break-before: always"></div>`,
+		},
+		{
+			in:       `<div style="page-break-inside: avoid;"></div>`,
+			expected: `<div style="page-break-inside: avoid"></div>`,
+		},
+		{
+			in: `<div style="perspective: 100px;"></div><div style=` +
+				`"perspective: none;"></div>`,
+			expected: `<div style="perspective: 100px"></div><div style=` +
+				`"perspective: none"></div>`,
+		},
+		{
+			in:       `<div style="perspective-origin: left;"></div>`,
+			expected: `<div style="perspective-origin: left"></div>`,
+		},
+		{
+			in:       `<div style="pointer-events: auto;"></div>`,
+			expected: `<div style="pointer-events: auto"></div>`,
+		},
+		{
+			in:       `<div style="position: absolute;"></div>`,
+			expected: `<div style="position: absolute"></div>`,
+		},
+		{
+			in:       `<div style="quotes: '‹' '›';"></div>`,
+			expected: `<div style="quotes: &#39;‹&#39; &#39;›&#39;"></div>`,
+		},
+		{
+			in:       `<div style="resize: both;"></div>`,
+			expected: `<div style="resize: both"></div>`,
+		},
+		{
+			in:       `<div style="right: 10px;"></div>`,
+			expected: `<div style="right: 10px"></div>`,
+		},
+		{
+			in:       `<div style="scroll-behavior: smooth;"></div>`,
+			expected: `<div style="scroll-behavior: smooth"></div>`,
+		},
+		{
+			in: `<div style="tab-size: 16;"></div><div style="tab-size:` +
+				` initial;"></div>`,
+			expected: `<div style="tab-size: 16"></div><div style="tab-size:` +
+				` initial"></div>`,
+		},
+		{
+			in:       `<div style="table-layout: fixed;"></div>`,
+			expected: `<div style="table-layout: fixed"></div>`,
+		},
+		{
+			in:       `<div style="text-align: justify;"></div>`,
+			expected: `<div style="text-align: justify"></div>`,
+		},
+		{
+			in:       `<div style="text-align-last: justify;"></div>`,
+			expected: `<div style="text-align-last: justify"></div>`,
+		},
+		{
+			in: `<div style="text-combine-upright: none;"></div><div` +
+				` style="text-combine-upright: digits 2"></div>`,
+			expected: `<div style="text-combine-upright: none"></div><div ` +
+				`style="text-combine-upright: digits 2"></div>`,
+		},
+		{
+			in: `<div style="text-decoration: underline underline;">` +
+				`</div><div style="text-decoration: initial"></div>`,
+			expected: `<div style="text-decoration: underline underline">` +
+				`</div><div style="text-decoration: initial"></div>`,
+		},
+		{
+			in:       `<div style="text-decoration-color: red;"></div>`,
+			expected: `<div style="text-decoration-color: red"></div>`,
+		},
+		{
+			in: `<div style="text-decoration-line: underline ` +
+				`underline;"></div>`,
+			expected: `<div style="text-decoration-line: underline ` +
+				`underline"></div>`,
+		},
+		{
+			in:       `<div style="text-decoration-style: solid;"></div>`,
+			expected: `<div style="text-decoration-style: solid"></div>`,
+		},
+		{
+			in: `<div style="text-indent: 30%;"></div><div style=` +
+				`"text-indent: initial"></div>`,
+			expected: `<div style="text-indent: 30%"></div><div style=` +
+				`"text-indent: initial"></div>`,
+		},
+		{
+			in:       `<div style="text-orientation: mixed"></div>`,
+			expected: `<div style="text-orientation: mixed"></div>`,
+		},
+		{
+			in:       `<div style="text-justify: inter-word;"></div>`,
+			expected: `<div style="text-justify: inter-word"></div>`,
+		},
+		{
+			in: `<div style="text-overflow: ellipsis;"></div><div ` +
+				`style="text-overflow: 'something'"></div>`,
+			expected: `<div style="text-overflow: ellipsis"></div><div ` +
+				`style="text-overflow: &#39;something&#39;"></div>`,
+		},
+		{
+			in:       `<div style="text-shadow: 2px 2px #ff0000;"></div>`,
+			expected: `<div style="text-shadow: 2px 2px #ff0000"></div>`,
+		},
+		{
+			in:       `<div style="text-transform: uppercase;"></div>`,
+			expected: `<div style="text-transform: uppercase"></div>`,
+		},
+		{
+			in:       `<div style="top: 150px;"></div>`,
+			expected: `<div style="top: 150px"></div>`,
+		},
+		{
+			in: `<div style="transform: scaleY(1.5);"></div><div ` +
+				`style="transform: perspective(20px);"></div>`,
+			expected: `<div style="transform: scaleY(1.5)"></div><div ` +
+				`style="transform: perspective(20px)"></div>`,
+		},
+		{
+			in:       `<div style="transform-origin: 40% 40%;"></div>`,
+			expected: `<div style="transform-origin: 40% 40%"></div>`,
+		},
+		{
+			in:       `<div style="transform-style: preserve-3d;"></div>`,
+			expected: `<div style="transform-style: preserve-3d"></div>`,
+		},
+		{
+			in:       `<div style="transition: width 2s;"></div>`,
+			expected: `<div style="transition: width 2s"></div>`,
+		},
+		{
+			in: `<div style="transition-delay: 2s;"></div><div ` +
+				`style="transition-delay: initial;"></div>`,
+			expected: `<div style="transition-delay: 2s"></div><div ` +
+				`style="transition-delay: initial"></div>`,
+		},
+		{
+			in: `<div style="transition-duration: 2s;"></div><div ` +
+				`style="transition-duration: initial;"></div>`,
+			expected: `<div style="transition-duration: 2s"></div><div ` +
+				`style="transition-duration: initial"></div>`,
+		},
+		{
+			in: `<div style="transition-property: width;"></div><div ` +
+				`style="transition-property: initial;"></div>`,
+			expected: `<div style="transition-property: width"></div><div ` +
+				`style="transition-property: initial"></div>`,
+		},
+		{
+			in: `<div style="transition-timing-function: linear;">` +
+				`</div>`,
+			expected: `<div style="transition-timing-function: linear">` +
+				`</div>`,
+		},
+		{
+			in:       `<div style="unicode-bidi: bidi-override;"></div>`,
+			expected: `<div style="unicode-bidi: bidi-override"></div>`,
+		},
+		{
+			in:       `<div style="user-select: none;"></div>`,
+			expected: `<div style="user-select: none"></div>`,
+		},
+		{
+			in:       `<div style="vertical-align: text-bottom;"></div>`,
+			expected: `<div style="vertical-align: text-bottom"></div>`,
+		},
+		{
+			in:       `<div style="visibility: visible;"></div>`,
+			expected: `<div style="visibility: visible"></div>`,
+		},
+		{
+			in:       `<div style="white-space: normal;"></div>`,
+			expected: `<div style="white-space: normal"></div>`,
+		},
+		{
+			in: `<div style="width: 130px;"></div><div style="width: ` +
+				`auto;"></div>`,
+			expected: `<div style="width: 130px"></div><div style="width: ` +
+				`auto"></div>`,
+		},
+		{
+			in:       `<div style="word-break: break-all;"></div>`,
+			expected: `<div style="word-break: break-all"></div>`,
+		},
+		{
+			in: `<div style="word-spacing: 30px;"></div><div style=` +
+				`"word-spacing: normal"></div>`,
+			expected: `<div style="word-spacing: 30px"></div><div style=` +
+				`"word-spacing: normal"></div>`,
+		},
+		{
+			in:       `<div style="word-wrap: break-word;"></div>`,
+			expected: `<div style="word-wrap: break-word"></div>`,
+		},
+		{
+			in:       `<div style="writing-mode: vertical-rl;"></div>`,
+			expected: `<div style="writing-mode: vertical-rl"></div>`,
+		},
+		{
+			in: `<div style="z-index: -1;"></div><div style="z-index:` +
+				` auto;"></div>`,
+			expected: `<div style="z-index: -1"></div><div style="z-index:` +
+				` auto"></div>`,
+		},
+	}
+
+	p := UGCPolicy()
+	p.AllowStyles("nonexistentStyle", "align-content", "align-items",
+		"align-self", "all", "animation", "animation-delay",
+		"animation-direction", "animation-duration", "animation-fill-mode",
+		"animation-iteration-count", "animation-name", "animation-play-state",
+		"animation-timing-function", "backface-visibility", "background",
+		"background-attachment", "background-blend-mode", "background-clip",
+		"background-color", "background-image", "background-origin",
+		"background-position", "background-repeat", "background-size",
+		"border", "border-bottom", "border-bottom-color",
+		"border-bottom-left-radius", "border-bottom-right-radius",
+		"border-bottom-style", "border-bottom-width", "border-collapse",
+		"border-color", "border-image", "border-image-outset",
+		"border-image-repeat", "border-image-slice", "border-image-source",
+		"border-image-width", "border-left", "border-left-color",
+		"border-left-style", "border-left-width", "border-radius",
+		"border-right", "border-right-color", "border-right-style",
+		"border-right-width", "border-spacing", "border-style", "border-top",
+		"border-top-color", "border-top-left-radius",
+		"border-top-right-radius", "border-top-style", "border-top-width",
+		"border-width", "bottom", "box-decoration-break", "box-shadow",
+		"box-sizing", "break-after", "break-before", "break-inside",
+		"caption-side", "caret-color", "clear", "clip", "color",
+		"column-count", "column-fill", "column-gap", "column-rule",
+		"column-rule-color", "column-rule-style", "column-rule-width",
+		"column-span", "column-width", "columns", "cursor", "direction",
+		"display", "empty-cells", "filter", "flex", "flex-basis",
+		"flex-direction", "flex-flow", "flex-grow", "flex-shrink",
+		"flex-wrap", "float", "font", "font-family", "font-kerning",
+		"font-language-override", "font-size", "font-size-adjust",
+		"font-stretch", "font-style", "font-synthesis", "font-variant",
+		"font-variant-caps", "font-variant-position", "font-weight", "grid",
+		"grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
+		"grid-column", "grid-column-end", "grid-column-gap",
+		"grid-column-start", "grid-gap", "grid-row", "grid-row-end",
+		"grid-row-gap", "grid-row-start", "grid-template",
+		"grid-template-areas", "grid-template-columns", "grid-template-rows",
+		"hanging-punctuation", "height", "hyphens", "image-rendering",
+		"isolation", "justify-content", "left", "letter-spacing", "line-break",
+		"line-height", "list-style", "list-style-image", "list-style-position",
+		"list-style-type", "margin", "margin-bottom", "margin-left",
+		"margin-right", "margin-top", "max-height", "max-width", "min-height",
+		"min-width", "mix-blend-mode", "object-fit", "object-position",
+		"opacity", "order", "orphans", "outline", "outline-color",
+		"outline-offset", "outline-style", "outline-width", "overflow",
+		"overflow-wrap", "overflow-x", "overflow-y", "padding",
+		"padding-bottom", "padding-left", "padding-right", "padding-top",
+		"page-break-after", "page-break-before", "page-break-inside",
+		"perspective", "perspective-origin", "pointer-events", "position",
+		"quotes", "resize", "right", "scroll-behavior", "tab-size",
+		"table-layout", "text-align", "text-align-last",
+		"text-combine-upright", "text-decoration", "text-decoration-color",
+		"text-decoration-line", "text-decoration-style", "text-indent",
+		"text-justify", "text-orientation", "text-overflow", "text-shadow",
+		"text-transform", "top", "transform", "transform-origin",
+		"transform-style", "transition", "transition-delay",
+		"transition-duration", "transition-property",
+		"transition-timing-function", "unicode-bidi", "user-select",
+		"vertical-align", "visibility", "white-space", "widows", "width",
+		"word-break", "word-spacing", "word-wrap", "writing-mode",
+		"z-index").Globally()
+	p.RequireParseableURLs(true)
+
+	// These tests are run concurrently to enable the race detector to pick up
+	// potential issues
+	wg := sync.WaitGroup{}
+	wg.Add(len(tests))
+	for ii, tt := range tests {
+		go func(ii int, tt test) {
+			out := p.Sanitize(tt.in)
+			if out != tt.expected {
+				t.Errorf(
+					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
+					ii,
+					tt.in,
+					out,
+					tt.expected,
+				)
+			}
+			wg.Done()
+		}(ii, tt)
+	}
+	wg.Wait()
+}
+
+func TestUnicodePoints(t *testing.T) {
+
+	tests := []test{
+		{
+			in:       `<div style="color: \72 ed;"></div>`,
+			expected: `<div style="color: \72 ed"></div>`,
+		},
+		{
+			in:       `<div style="color: \0072 ed;"></div>`,
+			expected: `<div style="color: \0072 ed"></div>`,
+		},
+		{
+			in:       `<div style="color: \000072 ed;"></div>`,
+			expected: `<div style="color: \000072 ed"></div>`,
+		},
+		{
+			in:       `<div style="color: \000072ed;"></div>`,
+			expected: `<div style="color: \000072ed"></div>`,
+		},
+		{
+			in:       `<div style="color: \100072ed;"></div>`,
+			expected: `<div></div>`,
+		},
+	}
+
+	p := UGCPolicy()
+	p.AllowStyles("color").Globally()
+	p.RequireParseableURLs(true)
+
+	// These tests are run concurrently to enable the race detector to pick up
+	// potential issues
+	wg := sync.WaitGroup{}
+	wg.Add(len(tests))
+	for ii, tt := range tests {
+		go func(ii int, tt test) {
+			out := p.Sanitize(tt.in)
+			if out != tt.expected {
+				t.Errorf(
+					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
+					ii,
+					tt.in,
+					out,
+					tt.expected,
+				)
+			}
+			wg.Done()
+		}(ii, tt)
+	}
+	wg.Wait()
+}
+
+func TestMatchingHandler(t *testing.T) {
+	truthHandler := func(value string) bool {
+		return true
+	}
+
+	tests := []test{
+		{
+			in:       `<div style="color: invalidValue"></div>`,
+			expected: `<div style="color: invalidValue"></div>`,
+		},
+	}
+
+	p := UGCPolicy()
+	p.AllowStyles("color").MatchingHandler(truthHandler).Globally()
+	p.RequireParseableURLs(true)
+
+	// These tests are run concurrently to enable the race detector to pick up
+	// potential issues
+	wg := sync.WaitGroup{}
+	wg.Add(len(tests))
+	for ii, tt := range tests {
+		go func(ii int, tt test) {
+			out := p.Sanitize(tt.in)
+			if out != tt.expected {
+				t.Errorf(
+					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
+					ii,
+					tt.in,
+					out,
+					tt.expected,
+				)
+			}
+			wg.Done()
+		}(ii, tt)
+	}
+	wg.Wait()
+}
+
+func TestStyleBlockHandler(t *testing.T) {
+	truthHandler := func(value string) bool {
+		return true
+	}
+
+	tests := []test{
+		{
+			in:       ``,
+			expected: ``,
+		},
+	}
+
+	p := UGCPolicy()
+	p.AllowStyles("color").MatchingHandler(truthHandler).Globally()
+	p.RequireParseableURLs(true)
+
+	// These tests are run concurrently to enable the race detector to pick up
+	// potential issues
+	wg := sync.WaitGroup{}
+	wg.Add(len(tests))
+	for ii, tt := range tests {
+		go func(ii int, tt test) {
+			out := p.Sanitize(tt.in)
+			if out != tt.expected {
+				t.Errorf(
+					"test %d failed;\ninput   : %s\noutput  : %s\nexpected: %s",
+					ii,
+					tt.in,
+					out,
+					tt.expected,
+				)
+			}
+			wg.Done()
+		}(ii, tt)
+	}
+	wg.Wait()
+}