Module: Dommy::Internal::CSS::PropertyRegistry
- Defined in:
- lib/dommy/internal/css/calc.rb,
lib/dommy/internal/css/property_registry.rb
Overview
The curated property table driving the cascade: which properties inherit, their initial values, computed-value transforms, and shorthand expansion. Properties outside the table degrade gracefully: their cascaded value is returned as-is (no inheritance, no normalization) rather than raising.
Defined Under Namespace
Classes: CalcUnresolvable, Property
Constant Summary collapse
- PROPERTIES =
{ "display" => Property.new(inherited: false, initial: "inline"), "visibility" => Property.new(inherited: true, initial: "visible"), "opacity" => Property.new(inherited: false, initial: "1"), "color" => Property.new(inherited: true, initial: "rgb(0, 0, 0)"), "background-color" => Property.new(inherited: false, initial: "rgba(0, 0, 0, 0)"), "font-size" => Property.new(inherited: true, initial: "16px"), "font-family" => Property.new(inherited: true, initial: "sans-serif"), "font-weight" => Property.new(inherited: true, initial: "400"), "font-style" => Property.new(inherited: true, initial: "normal"), "line-height" => Property.new(inherited: true, initial: "normal"), "text-align" => Property.new(inherited: true, initial: "start"), "text-decoration-line" => Property.new(inherited: false, initial: "none"), "white-space" => Property.new(inherited: true, initial: "normal"), "letter-spacing" => Property.new(inherited: true, initial: "normal"), "word-spacing" => Property.new(inherited: true, initial: "normal"), "cursor" => Property.new(inherited: true, initial: "auto"), "direction" => Property.new(inherited: true, initial: "ltr"), "text-transform" => Property.new(inherited: true, initial: "none"), "text-indent" => Property.new(inherited: true, initial: "0px"), "font-variant" => Property.new(inherited: true, initial: "normal"), "list-style-type" => Property.new(inherited: true, initial: "disc"), "list-style-position" => Property.new(inherited: true, initial: "outside"), "list-style-image" => Property.new(inherited: true, initial: "none"), "tab-size" => Property.new(inherited: true, initial: "8"), "z-index" => Property.new(inherited: false, initial: "auto"), "position" => Property.new(inherited: false, initial: "static"), # Color longhands of the border/outline/text-decoration shorthands: # registered so they normalize to rgb() and resolve currentColor (the # initial of each). Width/style longhands stay unregistered (passed # through as specified — Dommy applies no style-dependent used-value # rule like "width is 0 when style is none"). "border-top-color" => Property.new(inherited: false, initial: "currentColor"), "border-right-color" => Property.new(inherited: false, initial: "currentColor"), "border-bottom-color" => Property.new(inherited: false, initial: "currentColor"), "border-left-color" => Property.new(inherited: false, initial: "currentColor"), "outline-color" => Property.new(inherited: false, initial: "currentColor"), "text-decoration-color" => Property.new(inherited: false, initial: "currentColor"), "text-decoration-style" => Property.new(inherited: false, initial: "solid"), "flex-grow" => Property.new(inherited: false, initial: "0"), "flex-shrink" => Property.new(inherited: false, initial: "1"), "flex-basis" => Property.new(inherited: false, initial: "auto"), "flex-direction" => Property.new(inherited: false, initial: "row"), "flex-wrap" => Property.new(inherited: false, initial: "nowrap"), }.freeze
- COLOR_PROPERTIES =
%w[ color background-color border-top-color border-right-color border-bottom-color border-left-color outline-color text-decoration-color ].freeze
- FONT_WEIGHT_KEYWORDS =
{"normal" => "400", "bold" => "700"}.freeze
- TEXT_DECORATION_LINES =
text-decoration sub-property keyword sets (the shorthand classifies each token as a line, a style, or — failing both — a color).
%w[none underline overline line-through blink].freeze
- TEXT_DECORATION_STYLES =
%w[solid double dotted dashed wavy].freeze
- BORDER_SIDES =
%w[top right bottom left].freeze
- BORDER_STYLE_KEYWORDS =
%w[none hidden dotted dashed solid double groove ridge inset outset].freeze
- BORDER_WIDTH_KEYWORDS =
%w[thin medium thick].freeze
- BOX_SHORTHANDS =
{ "margin" => "margin-%s", "padding" => "padding-%s", "inset" => "%s", }.freeze
- BORDER_LONGHANDS =
Per-side longhands of the
borderfamily, in (width, style, color) order across the four sides — the targets a CSS-wide keyword onborderexpands to. BORDER_SIDES.flat_map do |side| %w[width style color].map { |kind| "border-#{side}-#{kind}" } end.freeze
- EXPANSION_TARGETS =
The longhands each curated shorthand covers — what a CSS-wide keyword (or an invalid var() substitution) applies to when it appears as the shorthand's whole value.
{ "margin" => %w[margin-top margin-right margin-bottom margin-left], "padding" => %w[padding-top padding-right padding-bottom padding-left], "inset" => %w[top right bottom left], "overflow" => %w[overflow-x overflow-y], "gap" => %w[row-gap column-gap], "background" => %w[background-color], "font" => %w[font-style font-weight font-size line-height font-family], "text-decoration" => %w[text-decoration-line text-decoration-style text-decoration-color], "border" => BORDER_LONGHANDS, "border-width" => %w[border-top-width border-right-width border-bottom-width border-left-width], "border-style" => %w[border-top-style border-right-style border-bottom-style border-left-style], "border-color" => %w[border-top-color border-right-color border-bottom-color border-left-color], "border-top" => %w[border-top-width border-top-style border-top-color], "border-right" => %w[border-right-width border-right-style border-right-color], "border-bottom" => %w[border-bottom-width border-bottom-style border-bottom-color], "border-left" => %w[border-left-width border-left-style border-left-color], "outline" => %w[outline-width outline-style outline-color], "flex" => %w[flex-grow flex-shrink flex-basis], "flex-flow" => %w[flex-direction flex-wrap], "list-style" => %w[list-style-type list-style-position list-style-image], "place-content" => %w[align-content justify-content], "place-items" => %w[align-items justify-items], "place-self" => %w[align-self justify-self], }.freeze
- ABSOLUTE_UNIT_PX =
px-per-unit for the absolute length units (CSS Values 4: 96px per inch). These need no layout, so a computed value can carry them as px.
{ "px" => 1.0, "pt" => 96.0 / 72, "pc" => 16.0, "in" => 96.0, "cm" => 96.0 / 2.54, "mm" => 96.0 / 25.4, # 1Q = 1/40 of a centimetre (CSS Values 4 §5.2). "q" => 96.0 / 2.54 / 40, }.freeze
- LENGTH_PATTERN =
/\A(-?\d+(?:\.\d+)?)(px|em|rem|pt|pc|in|cm|mm|q|vw|vh|vmin|vmax)\z/i
Class Method Summary collapse
-
.binary_operator_position?(previous) ⇒ Boolean
A +/- is a binary operator (vs a unary sign) when it follows a value: a number/dimension or a closing paren.
-
.border_triple(prefix, parts) ⇒ Object
Omitted border sub-properties reset to their initial (shorthand semantics): width medium, style none, color currentColor.
- .border_width_token?(low) ⇒ Boolean
-
.box_values(tokens) ⇒ Object
1 value -> all sides, 2 -> v/h, 3 -> t/h/b, 4 -> t/r/b/l.
-
.calc_tokenize(str) ⇒ Object
Tokens: "(" ")" "," operators, unit:, fn: (an ident, only valid before "("). Returns nil on an unexpected character — or when a binary +/- lacks the whitespace CSS Values 4 §10.1 requires on both sides (
1px+1pxis invalid; a unary sign after (/,/operator is fine).. -
.clamp_opacity(value) ⇒ Object
opacity computes to a number clamped to [0, 1]; a percentage maps to that fraction (css-color-4 §13).
-
.computed_value(name, value, font_size:, root_font_size:, viewport_width: nil, viewport_height: nil) ⇒ Object
The computed-value transform for one property.
- .evaluate_calc(value, **ctx) ⇒ Object
-
.expand(name, value) ⇒ Object
Expand a (possibly shorthand) declaration into [[name, value], ...] longhand pairs.
- .expand_border_all(value) ⇒ Object
-
.expand_border_box(kind, value) ⇒ Object
border-width|style|color: <box>— one kind across the four sides. - .expand_border_side(side, value) ⇒ Object
-
.expand_box(pattern, value) ⇒ Object
CSS box expansion: 1 value -> all sides, 2 -> v/h, 3 -> t/h/b, 4 -> t/r/b/l.
-
.expand_flex(value) ⇒ Object
flexper css-flexbox-1 §7.1.1: none -> 0 0 auto, auto -> 1 1 auto, a bare number -> grow 1 0%, etc. - .expand_flex_flow(value) ⇒ Object
-
.expand_font(value) ⇒ Object
Minimal
fontshorthand: [style] [weight][/ ] <family...>. - .expand_gap(value) ⇒ Object
-
.expand_list_style(value) ⇒ Object
list-style: <type> || <position> || <image>(any order). - .expand_outline(value) ⇒ Object
- .expand_overflow(value) ⇒ Object
-
.expand_place(suffix, value) ⇒ Object
place-content/items/self:
<align> [<justify>]; a single value applies to both. - .expand_text_decoration(value) ⇒ Object
- .expansion_targets(name) ⇒ Object
- .flex_one(token) ⇒ Object
- .flex_two(first, second) ⇒ Object
-
.format_number(number) ⇒ Object
A unitless calc result (e.g.
line-height: calc(1 + 0.5)), serialized without a unit and without a trailing ".0". - .format_px(number) ⇒ Object
-
.inherited?(name) ⇒ Boolean
Custom properties inherit by default (css-variables-1 §2); their initial value is the guaranteed-invalid value (nil here).
- .initial(name) ⇒ Object
- .known?(name) ⇒ Boolean
- .number?(token) ⇒ Boolean
-
.parse_border_shorthand(value) ⇒ Object
Classify the tokens of a
border/border-<side>/outlinevalue into style:, color: (order-independent, omitted parts nil). -
.percentage_of(value, base) ⇒ Object
px for a "
%" value against basepx, or nil when not a percentage (or no base available). -
.resolve_length(value, **ctx) ⇒ Object
Resolve a bare "
" length to a px string, or nil when it isn't a single resolvable length. -
.resolve_length_px(value, font_size:, root_font_size:, viewport_width: nil, viewport_height: nil) ⇒ Object
The px magnitude (Float) of a single "
" length, or nil when the value isn't such a length or its base is unavailable. -
.split_tokens(value) ⇒ Object
Whitespace split that keeps parenthesized groups intact, so a color
rgb(0, 0, 0)or anurl(...)stays one token.
Class Method Details
.binary_operator_position?(previous) ⇒ Boolean
A +/- is a binary operator (vs a unary sign) when it follows a value: a number/dimension or a closing paren.
88 89 90 |
# File 'lib/dommy/internal/css/calc.rb', line 88 def binary_operator_position?(previous) previous == ")" || (previous.is_a?(Hash) && previous.key?(:num)) end |
.border_triple(prefix, parts) ⇒ Object
Omitted border sub-properties reset to their initial (shorthand semantics): width medium, style none, color currentColor.
405 406 407 408 409 |
# File 'lib/dommy/internal/css/property_registry.rb', line 405 def border_triple(prefix, parts) [["#{prefix}-width", parts[:width] || "medium"], ["#{prefix}-style", parts[:style] || "none"], ["#{prefix}-color", parts[:color] || "currentColor"]] end |
.border_width_token?(low) ⇒ Boolean
399 400 401 |
# File 'lib/dommy/internal/css/property_registry.rb', line 399 def border_width_token?(low) BORDER_WIDTH_KEYWORDS.include?(low) || low.match?(/\A[.\-\d]/) end |
.box_values(tokens) ⇒ Object
1 value -> all sides, 2 -> v/h, 3 -> t/h/b, 4 -> t/r/b/l.
373 374 375 376 377 378 379 380 |
# File 'lib/dommy/internal/css/property_registry.rb', line 373 def box_values(tokens) case tokens.size when 1 then [tokens[0]] * 4 when 2 then [tokens[0], tokens[1], tokens[0], tokens[1]] when 3 then [tokens[0], tokens[1], tokens[2], tokens[1]] else tokens[0, 4] end end |
.calc_tokenize(str) ⇒ Object
Tokens: "(" ")" "," operators, unit:, fn: (an ident, only
valid before "("). Returns nil on an unexpected character — or when a
binary +/- lacks the whitespace CSS Values 4 §10.1 requires on both
sides (1px+1px is invalid; a unary sign after (/,/operator is fine).
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/dommy/internal/css/calc.rb', line 47 def calc_tokenize(str) tokens = [] index = 0 length = str.length space_before = true # the opening boundary counts as whitespace while index < length char = str[index] if char.match?(/\s/) space_before = true index += 1 next end if (char == "+" || char == "-") && binary_operator_position?(tokens.last) space_after = index + 1 < length && str[index + 1].match?(/\s/) return nil unless space_before && space_after tokens << char elsif "+-*/(),".include?(char) tokens << char elsif (match = str[index..].match(/\A(\d*\.\d+|\d+\.?\d*)([a-z%]*)/i)) tokens << {num: match[1].to_f, unit: match[2].downcase} index += match[0].length space_before = false next elsif (match = str[index..].match(/\A[a-z]+/i)) tokens << {fn: match[0].downcase} index += match[0].length space_before = false next else return nil end space_before = false index += 1 end tokens end |
.clamp_opacity(value) ⇒ Object
opacity computes to a number clamped to [0, 1]; a percentage maps to that fraction (css-color-4 §13). Unparseable values pass through.
213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/dommy/internal/css/property_registry.rb', line 213 def clamp_opacity(value) text = value.to_s.strip number = if text.end_with?("%") Float(text[0..-2]) / 100.0 else Float(text) end format_number(number.clamp(0.0, 1.0)) rescue ArgumentError, TypeError value end |
.computed_value(name, value, font_size:, root_font_size:, viewport_width: nil, viewport_height: nil) ⇒ Object
The computed-value transform for one property. font_size /
root_font_size are the element's / root's computed font-size in px;
viewport_width / viewport_height the viewport in px. Together they
let a bare length resolve to px without layout (em/rem/absolute/vw/vh).
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/dommy/internal/css/property_registry.rb', line 194 def computed_value(name, value, font_size:, root_font_size:, viewport_width: nil, viewport_height: nil) return Color.normalize(value) if COLOR_PROPERTIES.include?(name) return FONT_WEIGHT_KEYWORDS.fetch(value.downcase, value) if name == "font-weight" return clamp_opacity(value) if name == "opacity" # A <percentage> line-height computes to px against the element's own # font-size (CSS2 §10.8.1), inherited as that length. A unitless # <number> is kept as the number — its whole point is to inherit # unitless and scale to each descendant's font-size. if name == "line-height" && (pct = percentage_of(value, font_size)) return pct end ctx = {font_size: font_size, root_font_size: root_font_size, viewport_width: , viewport_height: } evaluate_calc(value, **ctx) || resolve_length(value, **ctx) || value end |
.evaluate_calc(value, **ctx) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/dommy/internal/css/calc.rb', line 20 def evaluate_calc(value, **ctx) value = value.to_s.strip return nil unless value.match?(/\A(?:calc|min|max|clamp)\(/i) tokens = calc_tokenize(value) return nil unless tokens parser = CalcParser.new(tokens, ctx) kind, number = parser.parse_value return nil unless parser.done? kind == :length ? format_px(number) : format_number(number) rescue CalcUnresolvable nil end |
.expand(name, value) ⇒ Object
Expand a (possibly shorthand) declaration into [[name, value], ...]
longhand pairs. Cascade correctness is the point: a later shorthand
must reset the longhands it covers (e.g. background: url(x) resets
background-color to its initial). Shorthands outside this curated
set pass through unexpanded (documented divergence).
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/dommy/internal/css/property_registry.rb', line 139 def (name, value) value = value.to_s.strip case name when "margin", "padding", "inset" (BOX_SHORTHANDS[name], value) || [[name, value]] when "overflow" (value) when "gap" (value) when "background" [["background-color", Color.extract(value) || initial("background-color")]] when "font" (value) || [[name, value]] when "text-decoration" (value) when "border" (value) when "border-top", "border-right", "border-bottom", "border-left" (name.delete_prefix("border-"), value) when "border-width", "border-style", "border-color" (name.delete_prefix("border-"), value) || [[name, value]] when "outline" (value) when "flex" (value) when "flex-flow" (value) when "list-style" (value) when "place-content", "place-items", "place-self" (name.delete_prefix("place-"), value) else [[name, value]] end end |
.expand_border_all(value) ⇒ Object
411 412 413 414 |
# File 'lib/dommy/internal/css/property_registry.rb', line 411 def (value) parts = parse_border_shorthand(value) BORDER_SIDES.flat_map { |side| border_triple("border-#{side}", parts) } end |
.expand_border_box(kind, value) ⇒ Object
border-width|style|color: <box> — one kind across the four sides.
421 422 423 424 425 426 |
# File 'lib/dommy/internal/css/property_registry.rb', line 421 def (kind, value) tokens = split_tokens(value) return nil unless (1..4).cover?(tokens.size) BORDER_SIDES.zip(box_values(tokens)).map { |side, v| ["border-#{side}-#{kind}", v] } end |
.expand_border_side(side, value) ⇒ Object
416 417 418 |
# File 'lib/dommy/internal/css/property_registry.rb', line 416 def (side, value) border_triple("border-#{side}", parse_border_shorthand(value)) end |
.expand_box(pattern, value) ⇒ Object
CSS box expansion: 1 value -> all sides, 2 -> v/h, 3 -> t/h/b, 4 -> t/r/b/l. Returns nil for token counts outside 1..4 (or values with nested whitespace we can't split safely).
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/dommy/internal/css/property_registry.rb', line 272 def (pattern, value) tokens = value.split(/\s+/) return nil unless (1..4).cover?(tokens.size) && tokens.none? { |t| t.include?("(") } top, right, bottom, left = case tokens.size when 1 then [tokens[0]] * 4 when 2 then [tokens[0], tokens[1], tokens[0], tokens[1]] when 3 then [tokens[0], tokens[1], tokens[2], tokens[1]] else tokens end %w[top right bottom left].zip([top, right, bottom, left]).map do |side, v| [format(pattern, side), v] end end |
.expand_flex(value) ⇒ Object
flex per css-flexbox-1 §7.1.1: none -> 0 0 auto, auto -> 1 1 auto,
a bare number -> grow 1 0%, etc. Omitted parts take the shorthand's
reset values, not the longhand initials.
438 439 440 441 442 443 444 445 446 447 |
# File 'lib/dommy/internal/css/property_registry.rb', line 438 def (value) tokens = split_tokens(value) grow, shrink, basis = case tokens.size when 1 then flex_one(tokens[0]) when 2 then flex_two(tokens[0], tokens[1]) else tokens[0, 3] end [["flex-grow", grow], ["flex-shrink", shrink], ["flex-basis", basis]] end |
.expand_flex_flow(value) ⇒ Object
464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/dommy/internal/css/property_registry.rb', line 464 def (value) direction = nil wrap = nil split_tokens(value).each do |token| low = token.downcase if %w[row row-reverse column column-reverse].include?(low) direction = token elsif %w[nowrap wrap wrap-reverse].include?(low) wrap = token end end [["flex-direction", direction || "row"], ["flex-wrap", wrap || "nowrap"]] end |
.expand_font(value) ⇒ Object
Minimal font shorthand: [style] [weight]
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/dommy/internal/css/property_registry.rb', line 302 def (value) tokens = value.split(/\s+/) size_index = tokens.index { |t| t.match?(%r{\A\d+(?:\.\d+)?(px|em|rem|%)(/|\z)}i) } return nil unless size_index style = "normal" weight = "400" tokens[0...size_index].each do |token| case token.downcase when "italic", "oblique" then style = token.downcase when "bold", /\A[1-9]00\z/ then weight = FONT_WEIGHT_KEYWORDS.fetch(token.downcase, token) end end size, line_height = tokens[size_index].split("/", 2) family = tokens[(size_index + 1)..].join(" ") pairs = [ ["font-style", style], ["font-weight", weight], ["font-size", size], ["line-height", line_height || "normal"], ] pairs << ["font-family", family] unless family.empty? pairs end |
.expand_gap(value) ⇒ Object
293 294 295 296 |
# File 'lib/dommy/internal/css/property_registry.rb', line 293 def (value) row, column = value.split(/\s+/) [["row-gap", row], ["column-gap", column || row]] end |
.expand_list_style(value) ⇒ Object
list-style: <type> || <position> || <image> (any order). A none
sets both type and image to none (css-lists-3).
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
# File 'lib/dommy/internal/css/property_registry.rb', line 480 def (value) type = position = image = nil none = false split_tokens(value).each do |token| low = token.downcase if %w[inside outside].include?(low) position = token elsif low.start_with?("url(") image = token elsif low == "none" none = true else type = token end end type ||= "none" if none image ||= "none" if none [["list-style-type", type || "disc"], ["list-style-position", position || "outside"], ["list-style-image", image || "none"]] end |
.expand_outline(value) ⇒ Object
428 429 430 431 432 433 |
# File 'lib/dommy/internal/css/property_registry.rb', line 428 def (value) parts = parse_border_shorthand(value) [["outline-width", parts[:width] || "medium"], ["outline-style", parts[:style] || "none"], ["outline-color", parts[:color] || "currentColor"]] end |
.expand_overflow(value) ⇒ Object
288 289 290 291 |
# File 'lib/dommy/internal/css/property_registry.rb', line 288 def (value) x, y = value.split(/\s+/) [["overflow-x", x], ["overflow-y", y || x]] end |
.expand_place(suffix, value) ⇒ Object
place-content/items/self: <align> [<justify>]; a single value
applies to both. (Multi-keyword alignment values like safe center
are not split — a documented simplification.)
505 506 507 508 |
# File 'lib/dommy/internal/css/property_registry.rb', line 505 def (suffix, value) tokens = split_tokens(value) [["align-#{suffix}", tokens[0]], ["justify-#{suffix}", tokens[1] || tokens[0]]] end |
.expand_text_decoration(value) ⇒ Object
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/dommy/internal/css/property_registry.rb', line 329 def (value) lines = [] style = nil color = nil split_tokens(value).each do |token| low = token.downcase if TEXT_DECORATION_LINES.include?(low) lines << token elsif TEXT_DECORATION_STYLES.include?(low) style = token else color ||= token end end [["text-decoration-line", lines.empty? ? "none" : lines.join(" ")], ["text-decoration-style", style || "solid"], ["text-decoration-color", color || "currentColor"]] end |
.expansion_targets(name) ⇒ Object
126 |
# File 'lib/dommy/internal/css/property_registry.rb', line 126 def expansion_targets(name) = EXPANSION_TARGETS.fetch(name, [name]) |
.flex_one(token) ⇒ Object
449 450 451 452 453 454 455 456 |
# File 'lib/dommy/internal/css/property_registry.rb', line 449 def flex_one(token) case token.downcase when "none" then ["0", "0", "auto"] when "auto" then ["1", "1", "auto"] when "initial" then ["0", "1", "auto"] else number?(token) ? [token, "1", "0%"] : ["1", "1", token] end end |
.flex_two(first, second) ⇒ Object
458 459 460 |
# File 'lib/dommy/internal/css/property_registry.rb', line 458 def flex_two(first, second) number?(second) ? [first, second, "0%"] : [first, "1", second] end |
.format_number(number) ⇒ Object
A unitless calc result (e.g. line-height: calc(1 + 0.5)), serialized
without a unit and without a trailing ".0".
38 39 40 41 |
# File 'lib/dommy/internal/css/calc.rb', line 38 def format_number(number) rounded = number.round(5) rounded == rounded.to_i ? rounded.to_i.to_s : rounded.to_s end |
.format_px(number) ⇒ Object
262 263 264 265 |
# File 'lib/dommy/internal/css/property_registry.rb', line 262 def format_px(number) rounded = number.round(3) rounded == rounded.to_i ? "#{rounded.to_i}px" : "#{rounded}px" end |
.inherited?(name) ⇒ Boolean
Custom properties inherit by default (css-variables-1 §2); their initial value is the guaranteed-invalid value (nil here).
130 |
# File 'lib/dommy/internal/css/property_registry.rb', line 130 def inherited?(name) = name.start_with?("--") || PROPERTIES[name]&.inherited || false |
.initial(name) ⇒ Object
132 |
# File 'lib/dommy/internal/css/property_registry.rb', line 132 def initial(name) = PROPERTIES[name]&.initial |
.known?(name) ⇒ Boolean
124 |
# File 'lib/dommy/internal/css/property_registry.rb', line 124 def known?(name) = PROPERTIES.key?(name) |
.number?(token) ⇒ Boolean
462 |
# File 'lib/dommy/internal/css/property_registry.rb', line 462 def number?(token) = token.match?(/\A-?\d+(?:\.\d+)?\z/) |
.parse_border_shorthand(value) ⇒ Object
Classify the tokens of a border/border-<side>/outline value into
style:, color: (order-independent, omitted parts nil).
384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/dommy/internal/css/property_registry.rb', line 384 def parse_border_shorthand(value) parts = {width: nil, style: nil, color: nil} split_tokens(value).each do |token| low = token.downcase if parts[:style].nil? && BORDER_STYLE_KEYWORDS.include?(low) parts[:style] = token elsif parts[:width].nil? && border_width_token?(low) parts[:width] = token else parts[:color] ||= token end end parts end |
.percentage_of(value, base) ⇒ Object
px for a "base px, or nil when not a
percentage (or no base available).
228 229 230 231 232 233 |
# File 'lib/dommy/internal/css/property_registry.rb', line 228 def percentage_of(value, base) return nil unless base match = value.to_s.strip.match(/\A(-?\d+(?:\.\d+)?)%\z/) match && format_px(match[1].to_f / 100.0 * base) end |
.resolve_length(value, **ctx) ⇒ Object
Resolve a bare "
237 238 239 240 |
# File 'lib/dommy/internal/css/property_registry.rb', line 237 def resolve_length(value, **ctx) px = resolve_length_px(value, **ctx) px && format_px(px) end |
.resolve_length_px(value, font_size:, root_font_size:, viewport_width: nil, viewport_height: nil) ⇒ Object
The px magnitude (Float) of a single "
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/dommy/internal/css/property_registry.rb', line 246 def resolve_length_px(value, font_size:, root_font_size:, viewport_width: nil, viewport_height: nil) match = value.to_s.match(LENGTH_PATTERN) return nil unless match number = match[1].to_f case match[2].downcase when "em" then font_size && number * font_size when "rem" then root_font_size && number * root_font_size when "vw" then && number * / 100.0 when "vh" then && number * / 100.0 when "vmin" then && && number * [, ].min / 100.0 when "vmax" then && && number * [, ].max / 100.0 else number * ABSOLUTE_UNIT_PX[match[2].downcase] end end |
.split_tokens(value) ⇒ Object
Whitespace split that keeps parenthesized groups intact, so a color
rgb(0, 0, 0) or an url(...) stays one token.
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/dommy/internal/css/property_registry.rb', line 350 def split_tokens(value) tokens = [] current = +"" depth = 0 value.each_char do |char| if char == "(" depth += 1 current << char elsif char == ")" depth -= 1 if depth.positive? current << char elsif char.match?(/\s/) && depth.zero? tokens << current unless current.empty? current = +"" else current << char end end tokens << current unless current.empty? tokens end |