-
Notifications
You must be signed in to change notification settings - Fork 746
Description
Problem Statement
With the increasing complexity of CSS architectures and the push for more modular and declarative designs, there's a clear advantage in allowing styles to be derived based on other computed styles without relying on custom properties alone. This proposal seeks to introduce the self()
function to fetch and compute styles based on other properties of the same selector.
The self()
function
The self()
function will allow designers to fetch a specific computed property value of the same selector in which it is being used. This can reduce the reliance on custom properties for shared values between properties, and streamline the styling process.
Use Case
** This is just one use case among countless
Computing the natural nested border-radius
for a descendant of another rounded element.
Current Approach
parent {
--br-tl: 30px;
--br-tr: 48px;
--br-br: 82px;
--br-bl: 130px;
--p-t: 20px;
--p-b: 10px;
--p-r: 26px;
--p-l: 44px;
border-radius: var(--br-tl) var(--br-tr) var(--br-br) var(--br-bl);
padding: var(--p-t) var(--p-r) var(--p-b) var(--p-l);
--nested-radius:
calc(var(--br-tl) - var(--p-t))
calc(var(--br-tr) - var(--p-r))
calc(var(--br-br) - var(--p-b))
calc(var(--br-bl) - var(--p-l));
}
child {
border-radius: var(--nested-radius);
}
Using self()
function
parent {
border-radius: 30px 48px 82px 130px;
padding: 20px 10px 26px 44px;
--nested-radius:
calc(self(border-top-left-radius) - self(padding-top))
calc(self(border-top-right-radius) - self(padding-right))
calc(self(border-bottom-right-radius) - self(padding-bottom))
calc(self(border-bottom-left-radius) - self(padding-left));
}
child {
border-radius: var(--nested-radius);
}
With declarative functions
With the prospective introduction of declarative functions into CSS, this feature would be even more assistive, as functions could reference other properties without needing to have every related value passed as an argument to that function.
This is how that same example above could look, abstracting the logic away into a custom function so it can be re-used effectively as often as needed, only needing to declare the logic once within the function:
@custom-function --get-nested-radius {
result: calc(self(border-top-left-radius) - self(padding-top)) calc(self(border-top-right-radius) - self(padding-right)) calc(self(border-bottom-right-radius) - self(padding-bottom)) calc(self(border-bottom-left-radius) - self(padding-left));
}
parent {
border-radius: 30px 48px 82px 130px;
padding: 20px 10px 26px 44px;
--nested-radius: --get-nested-radius();
}
child {
border-radius: var(--nested-radius);
}
You can think about using self()
in declarative functions similarly to how one references this
in a prototype method in JavaScript so the function logic can reference the object itself and its other properties, like this:
String.prototype.isEmpty = function() { return this.length === 0; };
Handling circularity
Addressing circularity risks could be a significant challenge with this proposal. Fetching a property's value from itself or from another property that references the original property could lead to an endless loop, but I think there may be some approaches we could take to combat/prevent that.
Examples of circularity
padding-left: self(padding);
padding: self(padding-left);
…or slightly less obviously…
margin-top: self(padding-bottom);
padding-bottom: self(border-width);
border-width: calc(self(padding-top) + 2px);
padding-top: self(margin-top);
Proposed approaches to preventing circularity issues
-
Explicit Blocking: If the browser detects a circular reference when computing the
self()
value, it should block the circularity and revert to the initial or default value for that property. This behavior would be similar to the way browsers handle invalid values in CSS. -
Limit Depth: Implement a depth limit for the number of times
self()
can be used consecutively within a property. If this depth is reached, it defaults to the initial or default value. This can be similar to the stack overflow concept in programming, preventing endless loops. -
Error Handling: CSS could introduce a new way of handling errors where, if a circular reference is detected, a specific error state is entered for that element, allowing developers to handle it appropriately.
Conclusion
The self()
function has the potential to streamline CSS development and make it more modular. By allowing property values to be fetched from other properties in the same selector, it reduces the overhead of using custom properties for shared values. However, care needs to be taken to handle potential circular references, ensuring the system remains robust and predictable.