diff --git a/base/schema-ruby/test/inputs/schema/integer-type.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/integer-type.schema/default/TopLevel.rb
index 9bee068..286a12f 100644
--- a/base/schema-ruby/test/inputs/schema/integer-type.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/integer-type.schema/default/TopLevel.rb
@@ -20,14 +20,14 @@ module Types
 end
 
 class TopLevel < Dry::Struct
-  attribute :above_i32_max,  Types::Integer
-  attribute :below_i32_min,  Types::Integer
-  attribute :i32_range,      Types::Integer
-  attribute :large_bounds,   Types::Integer
-  attribute :only_maximum,   Types::Integer
-  attribute :only_minimum,   Types::Integer
-  attribute :small_negative, Types::Integer
-  attribute :small_positive, Types::Integer
+  attribute :above_i32_max,  Types::Integer.constrained(gteq: 0, lteq: 2147483648)
+  attribute :below_i32_min,  Types::Integer.constrained(gteq: -2147483649, lteq: 0)
+  attribute :i32_range,      Types::Integer.constrained(gteq: -2147483648, lteq: 2147483647)
+  attribute :large_bounds,   Types::Integer.constrained(gteq: -9007199254740991, lteq: 9007199254740991)
+  attribute :only_maximum,   Types::Integer.constrained(lteq: 0)
+  attribute :only_minimum,   Types::Integer.constrained(gteq: 0)
+  attribute :small_negative, Types::Integer.constrained(gteq: -100, lteq: 0)
+  attribute :small_positive, Types::Integer.constrained(gteq: 0, lteq: 100)
   attribute :unbounded,      Types::Integer
 
   def self.from_dynamic!(d)
diff --git a/base/schema-ruby/test/inputs/schema/minmax-integer.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/minmax-integer.schema/default/TopLevel.rb
index 495fef0..d9a5825 100644
--- a/base/schema-ruby/test/inputs/schema/minmax-integer.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/minmax-integer.schema/default/TopLevel.rb
@@ -21,13 +21,13 @@ end
 
 class TopLevel < Dry::Struct
   attribute :free,                 Types::Integer
-  attribute :intersection,         Types::Integer
-  attribute :max,                  Types::Integer
-  attribute :min,                  Types::Integer
-  attribute :minmax,               Types::Integer
-  attribute :min_max_intersection, Types::Integer
+  attribute :intersection,         Types::Integer.constrained(gteq: 4, lteq: 5)
+  attribute :max,                  Types::Integer.constrained(lteq: 5)
+  attribute :min,                  Types::Integer.constrained(gteq: 3)
+  attribute :minmax,               Types::Integer.constrained(gteq: 3, lteq: 5)
+  attribute :min_max_intersection, Types::Integer.constrained(gteq: 3, lteq: 5)
   attribute :min_max_union,        Types::Integer
-  attribute :union,                Types::Integer
+  attribute :union,                Types::Integer.constrained(gteq: 3, lteq: 6)
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
diff --git a/base/schema-ruby/test/inputs/schema/minmax.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/minmax.schema/default/TopLevel.rb
index 9b60d37..3fda06f 100644
--- a/base/schema-ruby/test/inputs/schema/minmax.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/minmax.schema/default/TopLevel.rb
@@ -21,13 +21,13 @@ end
 
 class TopLevel < Dry::Struct
   attribute :free,                 Types::Double
-  attribute :intersection,         Types::Double
-  attribute :max,                  Types::Double
-  attribute :min,                  Types::Double
-  attribute :minmax,               Types::Double
-  attribute :min_max_intersection, Types::Double
+  attribute :intersection,         Types::Double.constrained(gteq: 4, lteq: 5)
+  attribute :max,                  Types::Double.constrained(lteq: 5)
+  attribute :min,                  Types::Double.constrained(gteq: 3)
+  attribute :minmax,               Types::Double.constrained(gteq: 3, lteq: 5)
+  attribute :min_max_intersection, Types::Double.constrained(gteq: 3, lteq: 5)
   attribute :min_max_union,        Types::Double
-  attribute :union,                Types::Double
+  attribute :union,                Types::Double.constrained(gteq: 3, lteq: 6)
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
diff --git a/base/schema-ruby/test/inputs/schema/optional-const-ref.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/optional-const-ref.schema/default/TopLevel.rb
index ee15103..7be76f3 100644
--- a/base/schema-ruby/test/inputs/schema/optional-const-ref.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/optional-const-ref.schema/default/TopLevel.rb
@@ -51,12 +51,12 @@ end
 
 class TopLevel < Dry::Struct
   attribute :coordinates,          Types.Array(Coordinate).optional
-  attribute :count,                Types::Integer.optional
+  attribute :count,                Types::Integer.constrained(gteq: 1, lteq: 100).optional
   attribute :label,                Types::String.optional
   attribute :required_coordinates, Types.Array(Coordinate)
-  attribute :required_count,       Types::Integer
+  attribute :required_count,       Types::Integer.constrained(gteq: 1, lteq: 100)
   attribute :required_label,       Types::String
-  attribute :weight,               Types::Double.optional
+  attribute :weight,               Types::Double.constrained(gteq: 0.5, lteq: 99.5).optional
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
diff --git a/base/schema-ruby/test/inputs/schema/optional-constraints.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/optional-constraints.schema/default/TopLevel.rb
index 4debbb4..7a948ac 100644
--- a/base/schema-ruby/test/inputs/schema/optional-constraints.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/optional-constraints.schema/default/TopLevel.rb
@@ -22,11 +22,11 @@ module Types
 end
 
 class TopLevel < Dry::Struct
-  attribute :opt_double,   Types::Double.optional
-  attribute :opt_int,      Types::Integer.optional
+  attribute :opt_double,   Types::Double.constrained(gteq: 0.5, lteq: 99.5).optional
+  attribute :opt_int,      Types::Integer.constrained(gteq: 0, lteq: 100).optional
   attribute :opt_pattern,  Types::String.optional
   attribute :opt_string,   Types::String.optional
-  attribute :req_zero_min, Types::Integer
+  attribute :req_zero_min, Types::Integer.constrained(gteq: 0, lteq: 100)
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
diff --git a/base/schema-ruby/test/inputs/schema/schema-constraints.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/schema-constraints.schema/default/TopLevel.rb
index e26c00b..57a19a3 100644
--- a/base/schema-ruby/test/inputs/schema/schema-constraints.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/schema-constraints.schema/default/TopLevel.rb
@@ -22,7 +22,7 @@ end
 
 class TopLevel < Dry::Struct
   attribute :min_max_length, Types::String
-  attribute :percent,        Types::Double
+  attribute :percent,        Types::Double.constrained(gteq: 0, lteq: 1)
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
diff --git a/base/schema-ruby/test/inputs/schema/vega-lite.schema/default/TopLevel.rb b/head/schema-ruby/test/inputs/schema/vega-lite.schema/default/TopLevel.rb
index a46aa51..a9f8ccb 100644
--- a/base/schema-ruby/test/inputs/schema/vega-lite.schema/default/TopLevel.rb
+++ b/head/schema-ruby/test/inputs/schema/vega-lite.schema/default/TopLevel.rb
@@ -253,7 +253,7 @@ end
 #
 # Font weight for title text.
 class FontWeightUnion < Dry::Struct
-  attribute :double, Types::Double.optional
+  attribute :double, Types::Double.constrained(gteq: 100, lteq: 900).optional
   attribute :enum_1, Types::FontWeight.optional
 
   def self.from_dynamic!(d)
@@ -354,7 +354,7 @@ class MarkConfig < Dry::Struct
   attribute :align, Types::HorizontalAlign.optional
 
   # The rotation angle of the text, in degrees.
-  attribute :angle, Types::Double.optional
+  attribute :angle, Types::Double.constrained(gteq: 0, lteq: 360).optional
 
   # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
   #
@@ -398,13 +398,13 @@ class MarkConfig < Dry::Struct
   # The fill opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :fill_opacity, Types::Double.optional
+  attribute :fill_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The typeface to set the text in (e.g., `"Helvetica Neue"`).
   attribute :font, Types::String.optional
 
   # The font size, in pixels.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # The font style (e.g., `"italic"`).
   attribute :font_style, Types::FontStyle.optional
@@ -443,7 +443,7 @@ class MarkConfig < Dry::Struct
   #
   # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
   # `square` marks or layered `bar` charts and `1` otherwise.
-  attribute :opacity, Types::Double.optional
+  attribute :opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The orientation of a non-stacked bar, tick, area, and line charts.
   # The value is either horizontal (default) or vertical.
@@ -458,7 +458,7 @@ class MarkConfig < Dry::Struct
 
   # Polar coordinate radial offset, in pixels, of the text label from the origin determined
   # by the `x` and `y` properties.
-  attribute :radius, Types::Double.optional
+  attribute :radius, Types::Double.constrained(gteq: 0).optional
 
   # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
   # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
@@ -471,7 +471,7 @@ class MarkConfig < Dry::Struct
   # of the size value.
   #
   # __Default value:__ `30`
-  attribute :size, Types::Double.optional
+  attribute :size, Types::Double.constrained(gteq: 0).optional
 
   # Default Stroke Color.  This has higher precedence than config.color
   #
@@ -487,13 +487,13 @@ class MarkConfig < Dry::Struct
   # The stroke opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :stroke_opacity, Types::Double.optional
+  attribute :stroke_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The stroke width, in pixels.
-  attribute :stroke_width, Types::Double.optional
+  attribute :stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Depending on the interpolation type, sets the tension parameter (for line and area marks).
-  attribute :tension, Types::Double.optional
+  attribute :tension, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Placeholder text if the `text` channel is not specified
   attribute :text, Types::String.optional
@@ -743,15 +743,15 @@ class AxisConfig < Dry::Struct
   # The stroke opacity of grid (value between [0,1])
   #
   # __Default value:__ (`1` by default)
-  attribute :grid_opacity, Types::Double.optional
+  attribute :grid_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The grid width, in pixels.
-  attribute :grid_width, Types::Double.optional
+  attribute :grid_width, Types::Double.constrained(gteq: 0).optional
 
   # The rotation angle of the axis labels.
   #
   # __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
-  attribute :label_angle, Types::Double.optional
+  attribute :label_angle, Types::Double.constrained(gteq: -360, lteq: 360).optional
 
   # Indicates if labels should be hidden if they exceed the axis range. If `false `(the
   # default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
@@ -780,7 +780,7 @@ class AxisConfig < Dry::Struct
   attribute :label_font, Types::String.optional
 
   # The font size of the label, in pixels.
-  attribute :label_font_size, Types::Double.optional
+  attribute :label_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Maximum allowed pixel width of axis tick labels.
   attribute :label_limit, Types::Double.optional
@@ -830,10 +830,10 @@ class AxisConfig < Dry::Struct
   attribute :ticks, Types::Bool.optional
 
   # The size in pixels of axis ticks.
-  attribute :tick_size, Types::Double.optional
+  attribute :tick_size, Types::Double.constrained(gteq: 0).optional
 
   # The width, in pixels, of ticks.
-  attribute :tick_width, Types::Double.optional
+  attribute :tick_width, Types::Double.constrained(gteq: 0).optional
 
   # Horizontal text alignment of axis titles.
   attribute :title_align, Types::String.optional
@@ -851,7 +851,7 @@ class AxisConfig < Dry::Struct
   attribute :title_font, Types::String.optional
 
   # Font size of the title.
-  attribute :title_font_size, Types::Double.optional
+  attribute :title_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Font weight of the title. (e.g., `"bold"`).
   attribute :title_font_weight, Types.Instance(TitleFontWeight).optional
@@ -1021,15 +1021,15 @@ class VGAxisConfig < Dry::Struct
   # The stroke opacity of grid (value between [0,1])
   #
   # __Default value:__ (`1` by default)
-  attribute :grid_opacity, Types::Double.optional
+  attribute :grid_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The grid width, in pixels.
-  attribute :grid_width, Types::Double.optional
+  attribute :grid_width, Types::Double.constrained(gteq: 0).optional
 
   # The rotation angle of the axis labels.
   #
   # __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
-  attribute :label_angle, Types::Double.optional
+  attribute :label_angle, Types::Double.constrained(gteq: -360, lteq: 360).optional
 
   # Indicates if labels should be hidden if they exceed the axis range. If `false `(the
   # default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
@@ -1058,7 +1058,7 @@ class VGAxisConfig < Dry::Struct
   attribute :label_font, Types::String.optional
 
   # The font size of the label, in pixels.
-  attribute :label_font_size, Types::Double.optional
+  attribute :label_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Maximum allowed pixel width of axis tick labels.
   attribute :label_limit, Types::Double.optional
@@ -1103,10 +1103,10 @@ class VGAxisConfig < Dry::Struct
   attribute :ticks, Types::Bool.optional
 
   # The size in pixels of axis ticks.
-  attribute :tick_size, Types::Double.optional
+  attribute :tick_size, Types::Double.constrained(gteq: 0).optional
 
   # The width, in pixels, of ticks.
-  attribute :tick_width, Types::Double.optional
+  attribute :tick_width, Types::Double.constrained(gteq: 0).optional
 
   # Horizontal text alignment of axis titles.
   attribute :title_align, Types::String.optional
@@ -1124,7 +1124,7 @@ class VGAxisConfig < Dry::Struct
   attribute :title_font, Types::String.optional
 
   # Font size of the title.
-  attribute :title_font_size, Types::Double.optional
+  attribute :title_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Font weight of the title. (e.g., `"bold"`).
   attribute :title_font_weight, Types.Instance(TitleFontWeight).optional
@@ -1248,7 +1248,7 @@ class BarConfig < Dry::Struct
   attribute :align, Types::HorizontalAlign.optional
 
   # The rotation angle of the text, in degrees.
-  attribute :angle, Types::Double.optional
+  attribute :angle, Types::Double.constrained(gteq: 0, lteq: 360).optional
 
   # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
   #
@@ -1259,7 +1259,7 @@ class BarConfig < Dry::Struct
   # statisticians) or 1 (Vega-Lite Default, D3 example style).
   #
   # __Default value:__ `1`
-  attribute :bin_spacing, Types::Double.optional
+  attribute :bin_spacing, Types::Double.constrained(gteq: 0).optional
 
   # Default color.  Note that `fill` and `stroke` have higher precedence than `color` and
   # will override `color`.
@@ -1272,7 +1272,7 @@ class BarConfig < Dry::Struct
   # The default size of the bars on continuous scales.
   #
   # __Default value:__ `5`
-  attribute :continuous_band_size, Types::Double.optional
+  attribute :continuous_band_size, Types::Double.constrained(gteq: 0).optional
 
   # The mouse cursor used over the mark. Any valid [CSS cursor
   # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
@@ -1280,7 +1280,7 @@ class BarConfig < Dry::Struct
 
   # The size of the bars.  If unspecified, the default size is  `bandSize-1`,
   # which provides 1 pixel offset between bars.
-  attribute :discrete_band_size, Types::Double.optional
+  attribute :discrete_band_size, Types::Double.constrained(gteq: 0).optional
 
   # The horizontal offset, in pixels, between the text label and its anchor point. The offset
   # is applied after rotation by the _angle_ property.
@@ -1307,13 +1307,13 @@ class BarConfig < Dry::Struct
   # The fill opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :fill_opacity, Types::Double.optional
+  attribute :fill_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The typeface to set the text in (e.g., `"Helvetica Neue"`).
   attribute :font, Types::String.optional
 
   # The font size, in pixels.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # The font style (e.g., `"italic"`).
   attribute :font_style, Types::FontStyle.optional
@@ -1352,7 +1352,7 @@ class BarConfig < Dry::Struct
   #
   # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
   # `square` marks or layered `bar` charts and `1` otherwise.
-  attribute :opacity, Types::Double.optional
+  attribute :opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The orientation of a non-stacked bar, tick, area, and line charts.
   # The value is either horizontal (default) or vertical.
@@ -1367,7 +1367,7 @@ class BarConfig < Dry::Struct
 
   # Polar coordinate radial offset, in pixels, of the text label from the origin determined
   # by the `x` and `y` properties.
-  attribute :radius, Types::Double.optional
+  attribute :radius, Types::Double.constrained(gteq: 0).optional
 
   # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
   # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
@@ -1380,7 +1380,7 @@ class BarConfig < Dry::Struct
   # of the size value.
   #
   # __Default value:__ `30`
-  attribute :size, Types::Double.optional
+  attribute :size, Types::Double.constrained(gteq: 0).optional
 
   # Default Stroke Color.  This has higher precedence than config.color
   #
@@ -1396,13 +1396,13 @@ class BarConfig < Dry::Struct
   # The stroke opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :stroke_opacity, Types::Double.optional
+  attribute :stroke_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The stroke width, in pixels.
-  attribute :stroke_width, Types::Double.optional
+  attribute :stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Depending on the interpolation type, sets the tension parameter (for line and area marks).
-  attribute :tension, Types::Double.optional
+  attribute :tension, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Placeholder text if the `text` channel is not specified
   attribute :text, Types::String.optional
@@ -1551,7 +1551,7 @@ class LegendConfig < Dry::Struct
   attribute :fill_color, Types::String.optional
 
   # The height of the gradient, in pixels.
-  attribute :gradient_height, Types::Double.optional
+  attribute :gradient_height, Types::Double.constrained(gteq: 0).optional
 
   # Text baseline for color ramp gradient labels.
   attribute :gradient_label_baseline, Types::String.optional
@@ -1566,10 +1566,10 @@ class LegendConfig < Dry::Struct
   attribute :gradient_stroke_color, Types::String.optional
 
   # The width of the gradient stroke, in pixels.
-  attribute :gradient_stroke_width, Types::Double.optional
+  attribute :gradient_stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # The width of the gradient, in pixels.
-  attribute :gradient_width, Types::Double.optional
+  attribute :gradient_width, Types::Double.constrained(gteq: 0).optional
 
   # The alignment of the legend label, can be left, middle or right.
   attribute :label_align, Types::String.optional
@@ -1586,13 +1586,13 @@ class LegendConfig < Dry::Struct
   # The font size of legend label.
   #
   # __Default value:__ `10`.
-  attribute :label_font_size, Types::Double.optional
+  attribute :label_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Maximum allowed pixel width of axis tick labels.
   attribute :label_limit, Types::Double.optional
 
   # The offset of the legend label.
-  attribute :label_offset, Types::Double.optional
+  attribute :label_offset, Types::Double.constrained(gteq: 0).optional
 
   # The offset, in pixels, by which to displace the legend from the edge of the enclosing
   # group or data rectangle.
@@ -1628,10 +1628,10 @@ class LegendConfig < Dry::Struct
   attribute :symbol_color, Types::String.optional
 
   # The size of the legend symbol, in pixels.
-  attribute :symbol_size, Types::Double.optional
+  attribute :symbol_size, Types::Double.constrained(gteq: 0).optional
 
   # The width of the symbol's stroke.
-  attribute :symbol_stroke_width, Types::Double.optional
+  attribute :symbol_stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Default shape type (such as "circle") for legend symbols.
   attribute :symbol_type, Types::String.optional
@@ -2040,11 +2040,11 @@ class ScaleConfig < Dry::Struct
   # Default inner padding for `x` and `y` band-ordinal scales.
   #
   # __Default value:__ `0.1`
-  attribute :band_padding_inner, Types::Double.optional
+  attribute :band_padding_inner, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Default outer padding for `x` and `y` band-ordinal scales.
   # If not specified, by default, band scale's paddingOuter is paddingInner/2.
-  attribute :band_padding_outer, Types::Double.optional
+  attribute :band_padding_outer, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # If true, values that exceed the data domain are clamped to either the minimum or maximum
   # range value
@@ -2054,68 +2054,68 @@ class ScaleConfig < Dry::Struct
   #
   # __Default:__ `5` for continuous x-scale of a vertical bar and continuous y-scale of a
   # horizontal bar.; `0` otherwise.
-  attribute :continuous_padding, Types::Double.optional
+  attribute :continuous_padding, Types::Double.constrained(gteq: 0).optional
 
   # The default max value for mapping quantitative fields to bar's size/bandSize.
   #
   # If undefined (default), we will use the scale's `rangeStep` - 1.
-  attribute :max_band_size, Types::Double.optional
+  attribute :max_band_size, Types::Double.constrained(gteq: 0).optional
 
   # The default max value for mapping quantitative fields to text's size/fontSize.
   #
   # __Default value:__ `40`
-  attribute :max_font_size, Types::Double.optional
+  attribute :max_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Default max opacity for mapping a field to opacity.
   #
   # __Default value:__ `0.8`
-  attribute :max_opacity, Types::Double.optional
+  attribute :max_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Default max value for point size scale.
-  attribute :max_size, Types::Double.optional
+  attribute :max_size, Types::Double.constrained(gteq: 0).optional
 
   # Default max strokeWidth for strokeWidth  (or rule/line's size) scale.
   #
   # __Default value:__ `4`
-  attribute :max_stroke_width, Types::Double.optional
+  attribute :max_stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # The default min value for mapping quantitative fields to bar and tick's size/bandSize
   # scale with zero=false.
   #
   # __Default value:__ `2`
-  attribute :min_band_size, Types::Double.optional
+  attribute :min_band_size, Types::Double.constrained(gteq: 0).optional
 
   # The default min value for mapping quantitative fields to tick's size/fontSize scale with
   # zero=false
   #
   # __Default value:__ `8`
-  attribute :min_font_size, Types::Double.optional
+  attribute :min_font_size, Types::Double.constrained(gteq: 0).optional
 
   # Default minimum opacity for mapping a field to opacity.
   #
   # __Default value:__ `0.3`
-  attribute :min_opacity, Types::Double.optional
+  attribute :min_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Default minimum value for point size scale with zero=false.
   #
   # __Default value:__ `9`
-  attribute :min_size, Types::Double.optional
+  attribute :min_size, Types::Double.constrained(gteq: 0).optional
 
   # Default minimum strokeWidth for strokeWidth (or rule/line's size) scale with zero=false.
   #
   # __Default value:__ `1`
-  attribute :min_stroke_width, Types::Double.optional
+  attribute :min_stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Default outer padding for `x` and `y` point-ordinal scales.
   #
   # __Default value:__ `0.5`
-  attribute :point_padding, Types::Double.optional
+  attribute :point_padding, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Default range step for band and point scales of (1) the `y` channel
   # and (2) the `x` channel when the mark is not `text`.
   #
   # __Default value:__ `21`
-  attribute :range_step, Types::Double.optional.optional
+  attribute :range_step, Types::Double.constrained(gteq: 0).optional.optional
 
   # If true, rounds numeric output values to integers.
   # This can be helpful for snapping to the pixel grid.
@@ -2125,7 +2125,7 @@ class ScaleConfig < Dry::Struct
   # Default range step for `x` band and point scales of text marks.
   #
   # __Default value:__ `90`
-  attribute :text_x_range_step, Types::Double.optional
+  attribute :text_x_range_step, Types::Double.constrained(gteq: 0).optional
 
   # Use the source data range before aggregation as scale domain instead of aggregated data
   # for aggregate axis.
@@ -2725,7 +2725,7 @@ class VGMarkConfig < Dry::Struct
   attribute :align, Types::HorizontalAlign.optional
 
   # The rotation angle of the text, in degrees.
-  attribute :angle, Types::Double.optional
+  attribute :angle, Types::Double.constrained(gteq: 0, lteq: 360).optional
 
   # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
   #
@@ -2752,13 +2752,13 @@ class VGMarkConfig < Dry::Struct
   # The fill opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :fill_opacity, Types::Double.optional
+  attribute :fill_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The typeface to set the text in (e.g., `"Helvetica Neue"`).
   attribute :font, Types::String.optional
 
   # The font size, in pixels.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # The font style (e.g., `"italic"`).
   attribute :font_style, Types::FontStyle.optional
@@ -2797,7 +2797,7 @@ class VGMarkConfig < Dry::Struct
   #
   # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
   # `square` marks or layered `bar` charts and `1` otherwise.
-  attribute :opacity, Types::Double.optional
+  attribute :opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The orientation of a non-stacked bar, tick, area, and line charts.
   # The value is either horizontal (default) or vertical.
@@ -2812,7 +2812,7 @@ class VGMarkConfig < Dry::Struct
 
   # Polar coordinate radial offset, in pixels, of the text label from the origin determined
   # by the `x` and `y` properties.
-  attribute :radius, Types::Double.optional
+  attribute :radius, Types::Double.constrained(gteq: 0).optional
 
   # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
   # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
@@ -2825,7 +2825,7 @@ class VGMarkConfig < Dry::Struct
   # of the size value.
   #
   # __Default value:__ `30`
-  attribute :size, Types::Double.optional
+  attribute :size, Types::Double.constrained(gteq: 0).optional
 
   # Default Stroke Color.  This has higher precedence than config.color
   #
@@ -2841,13 +2841,13 @@ class VGMarkConfig < Dry::Struct
   # The stroke opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :stroke_opacity, Types::Double.optional
+  attribute :stroke_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The stroke width, in pixels.
-  attribute :stroke_width, Types::Double.optional
+  attribute :stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Depending on the interpolation type, sets the tension parameter (for line and area marks).
-  attribute :tension, Types::Double.optional
+  attribute :tension, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Placeholder text if the `text` channel is not specified
   attribute :text, Types::String.optional
@@ -2941,7 +2941,7 @@ class TextConfig < Dry::Struct
   attribute :align, Types::HorizontalAlign.optional
 
   # The rotation angle of the text, in degrees.
-  attribute :angle, Types::Double.optional
+  attribute :angle, Types::Double.constrained(gteq: 0, lteq: 360).optional
 
   # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
   #
@@ -2985,13 +2985,13 @@ class TextConfig < Dry::Struct
   # The fill opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :fill_opacity, Types::Double.optional
+  attribute :fill_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The typeface to set the text in (e.g., `"Helvetica Neue"`).
   attribute :font, Types::String.optional
 
   # The font size, in pixels.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # The font style (e.g., `"italic"`).
   attribute :font_style, Types::FontStyle.optional
@@ -3030,7 +3030,7 @@ class TextConfig < Dry::Struct
   #
   # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
   # `square` marks or layered `bar` charts and `1` otherwise.
-  attribute :opacity, Types::Double.optional
+  attribute :opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The orientation of a non-stacked bar, tick, area, and line charts.
   # The value is either horizontal (default) or vertical.
@@ -3045,7 +3045,7 @@ class TextConfig < Dry::Struct
 
   # Polar coordinate radial offset, in pixels, of the text label from the origin determined
   # by the `x` and `y` properties.
-  attribute :radius, Types::Double.optional
+  attribute :radius, Types::Double.constrained(gteq: 0).optional
 
   # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
   # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
@@ -3061,7 +3061,7 @@ class TextConfig < Dry::Struct
   # of the size value.
   #
   # __Default value:__ `30`
-  attribute :size, Types::Double.optional
+  attribute :size, Types::Double.constrained(gteq: 0).optional
 
   # Default Stroke Color.  This has higher precedence than config.color
   #
@@ -3077,13 +3077,13 @@ class TextConfig < Dry::Struct
   # The stroke opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :stroke_opacity, Types::Double.optional
+  attribute :stroke_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The stroke width, in pixels.
-  attribute :stroke_width, Types::Double.optional
+  attribute :stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Depending on the interpolation type, sets the tension parameter (for line and area marks).
-  attribute :tension, Types::Double.optional
+  attribute :tension, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Placeholder text if the `text` channel is not specified
   attribute :text, Types::String.optional
@@ -3183,12 +3183,12 @@ class TickConfig < Dry::Struct
   attribute :align, Types::HorizontalAlign.optional
 
   # The rotation angle of the text, in degrees.
-  attribute :angle, Types::Double.optional
+  attribute :angle, Types::Double.constrained(gteq: 0, lteq: 360).optional
 
   # The width of the ticks.
   #
   # __Default value:__  2/3 of rangeStep.
-  attribute :band_size, Types::Double.optional
+  attribute :band_size, Types::Double.constrained(gteq: 0).optional
 
   # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
   #
@@ -3232,13 +3232,13 @@ class TickConfig < Dry::Struct
   # The fill opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :fill_opacity, Types::Double.optional
+  attribute :fill_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The typeface to set the text in (e.g., `"Helvetica Neue"`).
   attribute :font, Types::String.optional
 
   # The font size, in pixels.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # The font style (e.g., `"italic"`).
   attribute :font_style, Types::FontStyle.optional
@@ -3277,7 +3277,7 @@ class TickConfig < Dry::Struct
   #
   # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
   # `square` marks or layered `bar` charts and `1` otherwise.
-  attribute :opacity, Types::Double.optional
+  attribute :opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The orientation of a non-stacked bar, tick, area, and line charts.
   # The value is either horizontal (default) or vertical.
@@ -3292,7 +3292,7 @@ class TickConfig < Dry::Struct
 
   # Polar coordinate radial offset, in pixels, of the text label from the origin determined
   # by the `x` and `y` properties.
-  attribute :radius, Types::Double.optional
+  attribute :radius, Types::Double.constrained(gteq: 0).optional
 
   # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
   # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
@@ -3305,7 +3305,7 @@ class TickConfig < Dry::Struct
   # of the size value.
   #
   # __Default value:__ `30`
-  attribute :size, Types::Double.optional
+  attribute :size, Types::Double.constrained(gteq: 0).optional
 
   # Default Stroke Color.  This has higher precedence than config.color
   #
@@ -3321,13 +3321,13 @@ class TickConfig < Dry::Struct
   # The stroke opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :stroke_opacity, Types::Double.optional
+  attribute :stroke_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The stroke width, in pixels.
-  attribute :stroke_width, Types::Double.optional
+  attribute :stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # Depending on the interpolation type, sets the tension parameter (for line and area marks).
-  attribute :tension, Types::Double.optional
+  attribute :tension, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Placeholder text if the `text` channel is not specified
   attribute :text, Types::String.optional
@@ -3341,7 +3341,7 @@ class TickConfig < Dry::Struct
   # Thickness of the tick mark.
   #
   # __Default value:__  `1`
-  attribute :thickness, Types::Double.optional
+  attribute :thickness, Types::Double.constrained(gteq: 0).optional
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
@@ -3492,13 +3492,13 @@ class VGTitleConfig < Dry::Struct
   # Font size in pixels for title text.
   #
   # __Default value:__ `10`.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # Font weight for title text.
   attribute :font_weight, Types.Instance(FontWeightUnion).optional
 
   # The maximum allowed length in pixels of legend labels.
-  attribute :limit, Types::Double.optional
+  attribute :limit, Types::Double.constrained(gteq: 0).optional
 
   # Offset in pixels of the title from the chart body and axes.
   attribute :offset, Types::Double.optional
@@ -4238,7 +4238,7 @@ class BinParams < Dry::Struct
   # Maximum number of bins.
   #
   # __Default value:__ `6` for `row`, `column` and `shape` channels; `10` for other channels
-  attribute :maxbins, Types::Double.optional
+  attribute :maxbins, Types::Double.constrained(gteq: 2).optional
 
   # A minimum allowable step size (particularly useful for integer values).
   attribute :minstep, Types::Double.optional
@@ -4332,7 +4332,7 @@ end
 # DateTime definition object with `day`** should not be combined with `year`, `quarter`,
 # `month`, or `date`.
 class Day < Dry::Struct
-  attribute :double, Types::Double.optional
+  attribute :double, Types::Double.constrained(gteq: 1, lteq: 7).optional
   attribute :string, Types::String.optional
 
   def self.from_dynamic!(d)
@@ -4366,7 +4366,7 @@ end
 # (2) case-insensitive month name (e.g., `"January"`);  (3) case-insensitive, 3-character
 # short month name (e.g., `"Jan"`).
 class Month < Dry::Struct
-  attribute :double, Types::Double.optional
+  attribute :double, Types::Double.constrained(gteq: 1, lteq: 12).optional
   attribute :string, Types::String.optional
 
   def self.from_dynamic!(d)
@@ -4403,7 +4403,7 @@ end
 class DateTime < Dry::Struct
 
   # Integer value representing the date from 1-31.
-  attribute :date, Types::Double.optional
+  attribute :date, Types::Double.constrained(gteq: 1, lteq: 31).optional
 
   # Value representing the day of a week.  This can be one of: (1) integer value -- `1`
   # represents Monday; (2) case-insensitive day name (e.g., `"Monday"`);  (3)
@@ -4413,13 +4413,13 @@ class DateTime < Dry::Struct
   attribute :day, Types.Instance(Day).optional
 
   # Integer value representing the hour of a day from 0-23.
-  attribute :hours, Types::Double.optional
+  attribute :hours, Types::Double.constrained(gteq: 0, lteq: 23).optional
 
   # Integer value representing the millisecond segment of time.
-  attribute :milliseconds, Types::Double.optional
+  attribute :milliseconds, Types::Double.constrained(gteq: 0, lteq: 999).optional
 
   # Integer value representing the minute segment of time from 0-59.
-  attribute :minutes, Types::Double.optional
+  attribute :minutes, Types::Double.constrained(gteq: 0, lteq: 59).optional
 
   # One of: (1) integer value representing the month from `1`-`12`. `1` represents January;
   # (2) case-insensitive month name (e.g., `"January"`);  (3) case-insensitive, 3-character
@@ -4427,10 +4427,10 @@ class DateTime < Dry::Struct
   attribute :month, Types.Instance(Month).optional
 
   # Integer value representing the quarter of the year (from 1-4).
-  attribute :quarter, Types::Double.optional
+  attribute :quarter, Types::Double.constrained(gteq: 1, lteq: 4).optional
 
   # Integer value representing the second segment (0-59) of a time value
-  attribute :seconds, Types::Double.optional
+  attribute :seconds, Types::Double.constrained(gteq: 0, lteq: 59).optional
 
   # A boolean flag indicating if date time is in utc time. If false, the date time is in
   # local time
@@ -5080,7 +5080,7 @@ class Legend < Dry::Struct
   # A non-positive integer indicating z-index of the legend.
   # If zindex is 0, legend should be drawn behind all chart elements.
   # To put them in front, use zindex = 1.
-  attribute :zindex, Types::Double.optional
+  attribute :zindex, Types::Double.constrained(gteq: 0).optional
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
@@ -5671,7 +5671,7 @@ class Scale < Dry::Struct
   # __Default value:__ For _continuous_ scales, derived from the [scale
   # config](scale.html#config)'s `continuousPadding`.
   # For _band and point_ scales, see `paddingInner` and `paddingOuter`.
-  attribute :padding, Types::Double.optional
+  attribute :padding, Types::Double.constrained(gteq: 0).optional
 
   # The inner padding (spacing) within each band step of band scales, as a fraction of the
   # step size. This value must lie in the range [0,1].
@@ -5681,14 +5681,14 @@ class Scale < Dry::Struct
   #
   # __Default value:__ derived from the [scale config](scale.html#config)'s
   # `bandPaddingInner`.
-  attribute :padding_inner, Types::Double.optional
+  attribute :padding_inner, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The outer padding (spacing) at the ends of the range of band and point scales,
   # as a fraction of the step size. This value must lie in the range [0,1].
   #
   # __Default value:__ derived from the [scale config](scale.html#config)'s
   # `bandPaddingOuter` for band scales and `pointPadding` for point scales.
-  attribute :padding_outer, Types::Double.optional
+  attribute :padding_outer, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The range of the scale. One of:
   #
@@ -5728,7 +5728,7 @@ class Scale < Dry::Struct
   # __Warning__: If `rangeStep` is `null` and the cardinality of the scale's domain is higher
   # than `width` or `height`, the rangeStep might become less than one pixel and the mark
   # might not appear correctly.
-  attribute :range_step, Types::Double.optional.optional
+  attribute :range_step, Types::Double.constrained(gteq: 0).optional.optional
 
   # If `true`, rounds numeric output values to integers. This can be helpful for snapping to
   # the pixel grid.
@@ -6238,7 +6238,7 @@ class Header < Dry::Struct
   # The rotation angle of the header labels.
   #
   # __Default value:__ `0`.
-  attribute :label_angle, Types::Double.optional
+  attribute :label_angle, Types::Double.constrained(gteq: -360, lteq: 360).optional
 
   # A title for the field. If `null`, the title will be removed.
   #
@@ -7106,7 +7106,7 @@ class Axis < Dry::Struct
   # The rotation angle of the axis labels.
   #
   # __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
-  attribute :label_angle, Types::Double.optional
+  attribute :label_angle, Types::Double.constrained(gteq: -360, lteq: 360).optional
 
   # Indicates if labels should be hidden if they exceed the axis range. If `false `(the
   # default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
@@ -7188,7 +7188,7 @@ class Axis < Dry::Struct
   attribute :ticks, Types::Bool.optional
 
   # The size in pixels of axis ticks.
-  attribute :tick_size, Types::Double.optional
+  attribute :tick_size, Types::Double.constrained(gteq: 0).optional
 
   # A title for the field. If `null`, the title will be removed.
   #
@@ -7220,7 +7220,7 @@ class Axis < Dry::Struct
   #
   # __Default value:__ `1` (in front of the marks) for actual axis and `0` (behind the marks)
   # for grids.
-  attribute :zindex, Types::Double.optional
+  attribute :zindex, Types::Double.constrained(gteq: 0).optional
 
   def self.from_dynamic!(d)
     d = Types::Hash[d]
@@ -7842,7 +7842,7 @@ class MarkDef < Dry::Struct
   attribute :align, Types::HorizontalAlign.optional
 
   # The rotation angle of the text, in degrees.
-  attribute :angle, Types::Double.optional
+  attribute :angle, Types::Double.constrained(gteq: 0, lteq: 360).optional
 
   # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
   #
@@ -7889,13 +7889,13 @@ class MarkDef < Dry::Struct
   # The fill opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :fill_opacity, Types::Double.optional
+  attribute :fill_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The typeface to set the text in (e.g., `"Helvetica Neue"`).
   attribute :font, Types::String.optional
 
   # The font size, in pixels.
-  attribute :font_size, Types::Double.optional
+  attribute :font_size, Types::Double.constrained(gteq: 0).optional
 
   # The font style (e.g., `"italic"`).
   attribute :font_style, Types::FontStyle.optional
@@ -7934,7 +7934,7 @@ class MarkDef < Dry::Struct
   #
   # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
   # `square` marks or layered `bar` charts and `1` otherwise.
-  attribute :opacity, Types::Double.optional
+  attribute :opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The orientation of a non-stacked bar, tick, area, and line charts.
   # The value is either horizontal (default) or vertical.
@@ -7949,7 +7949,7 @@ class MarkDef < Dry::Struct
 
   # Polar coordinate radial offset, in pixels, of the text label from the origin determined
   # by the `x` and `y` properties.
-  attribute :radius, Types::Double.optional
+  attribute :radius, Types::Double.constrained(gteq: 0).optional
 
   # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
   # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
@@ -7962,7 +7962,7 @@ class MarkDef < Dry::Struct
   # of the size value.
   #
   # __Default value:__ `30`
-  attribute :size, Types::Double.optional
+  attribute :size, Types::Double.constrained(gteq: 0).optional
 
   # Default Stroke Color.  This has higher precedence than config.color
   #
@@ -7978,10 +7978,10 @@ class MarkDef < Dry::Struct
   # The stroke opacity (value between [0,1]).
   #
   # __Default value:__ `1`
-  attribute :stroke_opacity, Types::Double.optional
+  attribute :stroke_opacity, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # The stroke width, in pixels.
-  attribute :stroke_width, Types::Double.optional
+  attribute :stroke_width, Types::Double.constrained(gteq: 0).optional
 
   # A string or array of strings indicating the name of custom styles to apply to the mark. A
   # style is a named collection of mark property defaults defined within the [style
@@ -7997,7 +7997,7 @@ class MarkDef < Dry::Struct
   attribute :style, Types.Instance(Style).optional
 
   # Depending on the interpolation type, sets the tension parameter (for line and area marks).
-  attribute :tension, Types::Double.optional
+  attribute :tension, Types::Double.constrained(gteq: 0, lteq: 1).optional
 
   # Placeholder text if the `text` channel is not specified
   attribute :text, Types::String.optional
