-
Notifications
You must be signed in to change notification settings - Fork 63
Description
There are cases where components don't allow passing a CSS class to the root of the component, but expose styling of inner parts.
For example Radix has components with an invisible unstylable wrapper, like Alert and Tooltip that don't accept styling for the grouping component, but accept a class for inner parts like trigger, overlay, and content.
Current state
When writing a selector in Stylable, CSS class might be transformed to namespace or map to a different selector, but the intent to target something is preserved.
If we define an alert.st.css stylesheet today, with inner parts and try to use the alert root class as a selector then we get the alert__root class that won't be in the DOM, so it won't target and apply anything:
/* theme.st.css */
@st-import Alert from './alert.st.css';
Alert::trigger {} /* .alert__root .alert__trigger */
Alert::overlay {} /* .alert__root .alert__overlay */This means that we can try and model the style API to treat each inner part as a separate component by either:
1. Having a different stylesheet for each part
/* alert-trigger.st.css */
.root {}/* alert-overlay.st.css */
.root {}/* theme.st.css */
@st-import AlertTrigger from './alert-trigger.st.css';
@st-import AlertOverlay from './alert-overlay.st.css';
AlertTrigger {} /* .alertTrigger__root */
AlertOverlay {} /* .alertOverlay__root */2. Import inner parts
/* alert.st.css */
.root {}
.trigger {}
.overlay {}/* theme.st.css */
@st-import [trigger, overlay] from './alert.st.css';
.trigger {} /* .alert__trigger */
.overlay {} /* .alert__overlay */The second is probably better as it is less verbose on files and code, but it is still less good then having a component root to group inner parts as syntactic sugar, especially with some component stylesheets containing many parts.
Goals
- Group a set of parts together
- Mark a class to only exist for build time - transpiled away
Proposal
- New interface only class - A new stylable declaration
-st-type-onlyor maybe add an option to-st-extendsto accept a "none" value to indicate its not a runtime class and is not intended to target anything. - Can only be applied to root class (not sure there is a case for inner parts)
- Sticky interface - extending is basically stating that a new class is going to be set on the same DOM node as the extended class. Since we are defining a class that cannot exist in the DOM, Then extending such a class would mean the extending class is also an interface only class.
- transform away interface only class during the build process
- Report error when a selector subject is unstylable
- No runtime style API mapping for such classes
- Filter out interface classes from programmatic reflection APIs (transformIntoSelector, getAllClasses, etc.) - need to see how to differentiate between symbols and runtime classes
definition
/* alert.st.css */
.root {
-st-extends: (); /* interface only root */
}
.trigger {}
.overlay {}transform away interface only root
/* theme.st.css */
@st-import Alert from './alert.st.css';
Alert::trigger {} /* .alert__trigger */
Alert::overlay {} /* .alert__overlay */
/* report: unstylable selector subject */
Alert {}no runtime
/* alert.js */
import { classes } from `./alert.st.css`;
classes.root {} // undefined
classes.trigger {} // alert__trigger
classes.overlay {} // alert__overlayextend interface only class
/* super-alert.st.css */
@st-import Alert from './alert.st.css';
.root {
-st-extends: Alert; /* also set as interface only */
}
.part {};
.root::trigger {} /* .alert__trigger */
.root::overlay {} /* .alert__overlay */
.root::part {} /* .super-alert__part */
/* report: unstylable selector subject */
root {}Metadata
Metadata
Assignees
Labels
Type
Projects
Status