diff --git a/apps/docs/babel.config.js b/apps/docs/babel.config.js index d05e0ce5..e300c7b4 100644 --- a/apps/docs/babel.config.js +++ b/apps/docs/babel.config.js @@ -7,12 +7,18 @@ * @format */ +const path = require('path'); const isProd = process.env.NODE_ENV === 'production'; const options = { dev: !isProd, test: false, - stylexSheetName: isProd ? 'custom' : undefined, + stylexSheetName: '<>', + genConditionalClasses: true, + unstable_moduleResolution: { + type: 'commonJS', + rootDir: path.join(__dirname, '../..'), + }, }; module.exports = { diff --git a/apps/docs/components/FeatureCard.js b/apps/docs/components/FeatureCard.js new file mode 100644 index 00000000..2cc8dda3 --- /dev/null +++ b/apps/docs/components/FeatureCard.js @@ -0,0 +1,136 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import * as React from 'react'; +import * as stylex from '@stylexjs/stylex'; +import Link from '@docusaurus/Link'; + +let count = 0; + +function useId() { + const [id, setId] = React.useState(null); + React.useEffect(() => { + setId(`id-${++count}`); + }, []); + return id; +} + +export default function FeatureCard({ + to, + emoji, + title, + subtitle, + children, + style, +}) { + const titleId = useId(); + return ( + +
+
+ {emoji} +
+
+

+ {title} +

+

{subtitle}

+

{children}

+
+
+ + ); +} + +const LARGE = '@container (min-width: 540px)'; + +const styles = stylex.create({ + card: { + alignItems: 'center', + backgroundColor: 'var(--bg1)', + borderColor: 'hsla(var(--pink-h), var(--pink-s), var(--pink-l), 0.1)', + borderRadius: 32, + borderStyle: 'solid', + borderWidth: 1, + boxShadow: { + default: + '0 2px 4px hsla(var(--pink-h), var(--pink-s), var(--pink-l), 0.1)', + ':hover': + '0 2px 8px hsla(var(--pink-h), var(--pink-s), var(--pink-l), 0.5)', + }, + boxSizing: 'border-box', + color: { + default: 'inherit', + ':hover': 'inherit', + }, + textDecoration: { + default: 'none', + ':hover': 'none', + }, + containerType: 'inline-size', + display: 'flex', + gridRow: { + default: 'span 2', + '@container (max-width: 940px)': 'span 1', + }, + height: '100%', + justifyContent: 'center', + position: 'relative', + transitionProperty: 'box-shadow', + transitionDuration: '0.2s', + transitionTimingFunction: 'ease-in-out', + }, + layout: { + display: 'flex', + flexDirection: { + default: 'column', + [LARGE]: 'row', + }, + width: '100%', + alignItems: 'center', + justifyContent: { + default: 'center', + [LARGE]: 'flex-start', + }, + textAlign: { + default: 'center', + [LARGE]: 'left', + }, + padding: { + default: 16, + [LARGE]: 32, + }, + columnGap: 32, + }, + emoji: { + fontSize: '8rem', + alignSelf: 'center', + marginBlock: '-0.16em', + }, + title: { + fontSize: '2rem', + margin: 0, + }, + subTitle: { + fontSize: '1rem', + margin: 0, + marginTop: '0.2em', + color: 'var(--pink)', + fontWeight: 400, + }, + body: { + marginTop: '1em', + fontSize: '1rem', + lineHeight: 1.4, + opacity: 0.5, + }, +}); diff --git a/apps/docs/components/FeaturePile.js b/apps/docs/components/FeaturePile.js new file mode 100644 index 00000000..0d0d03ab --- /dev/null +++ b/apps/docs/components/FeaturePile.js @@ -0,0 +1,100 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import * as React from 'react'; +import * as stylex from '@stylexjs/stylex'; +import FeatureCard from './FeatureCard'; + +export default function FeaturePile() { + return ( +
+
+ + Scale new heights without being weighed down by the size of CSS + bundles. + + + You shouldn't need a crystal ball to know what styles are applied on + an element. + + + Styles can be passed around as props, and merged deterministically. It + all fits together. + + + The StyleX compiler bundles styles into a static CSS file. No runtime + style injection. + + + Safety first! Static types catch common styling mistakes in code. + +
+
+ ); +} + +const styles = stylex.create({ + container: { + width: '100%', + containerType: 'inline-size', + }, + grid: { + display: 'grid', + gridTemplateColumns: { + default: '1fr 1fr 1fr 1fr', + '@container (min-width: 940.1px) and (max-width: 1230px)': '1fr 1fr 1fr', + '@container (max-width: 940px)': '1fr', + }, + gridAutoRows: '1fr', + gap: 16, + width: '100%', + alignItems: 'center', + }, + double: { + gridColumn: { + default: null, + '@container (min-width: 940.1px)': 'span 2', + }, + gridRow: null, + }, + small: { + gridColumn: null, + gridRow: null, + }, + smallOnLarge: { + gridRow: { + default: null, + '@container (min-width: 940px) and (max-width: 1230px)': 'span 2', + }, + gridColumn: null, + }, +}); diff --git a/apps/docs/docs/learn/01-installation.mdx b/apps/docs/docs/learn/02-installation.mdx similarity index 60% rename from apps/docs/docs/learn/01-installation.mdx rename to apps/docs/docs/learn/02-installation.mdx index 59f61c4f..ffa9f512 100755 --- a/apps/docs/docs/learn/01-installation.mdx +++ b/apps/docs/docs/learn/02-installation.mdx @@ -66,105 +66,27 @@ done with any bundler that supports Babel, using the metadata generated by the StyleX plugin. See the API reference for more details on the `@stylexjs/babel-plugin` API. -Here's an example using the StyleX's Rollup plugin. - - - - -```tsx title="rollup.config.js" -import stylexPlugin from '@stylexjs/rollup-plugin'; - -const config = { - input: './index.js', - output: { - file: './.build/bundle.js', - format: 'es', - }, - // Ensure that the stylex plugin is used before Babel - plugins: [stylexPlugin({ - // Required. File path for the generated CSS file. - fileName: './.build/stylex.css' - // default: false - dev: false, - // prefix for all generated classNames - classNamePrefix: 'x', - // Required for CSS variable support - unstable_moduleResolution: { - // type: 'commonJS' | 'haste' - // default: 'commonJS' - type: 'commonJS', - // The absolute path to the root directory of your project - rootDir: __dirname, - }, - })], -}; - -export default config; -``` +To make this easier for commonly used packagers and meta-frameworks, StyleX +provides plugins for Webpack, Rollup, and Next.js. - +
+ Rollup - + -```tsx title="webpack.config.js" -const StylexPlugin = require('@stylexjs/webpack-plugin'); -const path = require('path'); + ```tsx title="rollup.config.js" + import stylexPlugin from '@stylexjs/rollup-plugin'; -const config = (env, argv) => ({ - entry: { - main: './src/index.js', - }, - output: { - path: path.resolve(__dirname, '.build'), - filename: '[name].js', - }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: 'babel-loader', - }, - ], - }, - plugins: [ + const config = { + input: './index.js', + output: { + file: './.build/bundle.js', + format: 'es', + }, // Ensure that the stylex plugin is used before Babel - new StylexPlugin({ - filename: 'styles.css', - // get webpack mode and set value for dev - dev: argv.mode === 'development', - // optional. default: 'x' - classNamePrefix: 'x', - // Required for CSS variable support - unstable_moduleResolution: { - // type: 'commonJS' | 'haste' - // default: 'commonJS' - type: 'commonJS', - // The absolute path to the root directory of your project - rootDir: __dirname, - }, - }), - ], - cache: true, -}); - -module.exports = config; -``` - - - - - -```tsx title=".babelrc.js" -import styleXPlugin from '@stylexjs/babel-plugin'; - -const {code, map, metadata} = await babel.transformAsync(inputCode, { - presets, - plugins: [ - ...plugins, - [styleXPlugin, { + plugins: [stylexPlugin({ // Required. File path for the generated CSS file. - stylexSheetName: './.build/stylex.css' + fileName: './.build/stylex.css' // default: false dev: false, // prefix for all generated classNames @@ -177,31 +99,99 @@ const {code, map, metadata} = await babel.transformAsync(inputCode, { // The absolute path to the root directory of your project rootDir: __dirname, }, - }], - ], -}); - -const collectedCSS = styleXPlugin.processStylexRules(metadata.stylex); -``` + })], + }; - + export default config; + ``` +
-
- -To make this easier for commonly used packagers and meta-frameworks, StyleX -provides plugins for Webpack, Rollup, and Next.js. +
+ Webpack -#### Rollup + - + ```tsx title="webpack.config.js" + const StylexPlugin = require('@stylexjs/webpack-plugin'); + const path = require('path'); -#### Webpack + const config = (env, argv) => ({ + entry: { + main: './src/index.js', + }, + output: { + path: path.resolve(__dirname, '.build'), + filename: '[name].js', + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: 'babel-loader', + }, + ], + }, + plugins: [ + // Ensure that the stylex plugin is used before Babel + new StylexPlugin({ + filename: 'styles.css', + // get webpack mode and set value for dev + dev: argv.mode === 'development', + // optional. default: 'x' + classNamePrefix: 'x', + // Required for CSS variable support + unstable_moduleResolution: { + // type: 'commonJS' | 'haste' + // default: 'commonJS' + type: 'commonJS', + // The absolute path to the root directory of your project + rootDir: __dirname, + }, + }), + ], + cache: true, + }); + + module.exports = config; + ``` + +
+ +
+ Next.js + + + + ```tsx title=".babelrc.js" + module.exports = { + presets: ['next/babel'], + plugins: [ + [ + '@stylexjs/babel-plugin', + { + dev: process.env.NODE_ENV === 'development', + stylexSheetName: '<>', + genConditionalClasses: true, + unstable_moduleResolution: { + type: 'commonJS', + rootDir: __dirname, + }, + }, + ], + ], + }; + ``` - + ```tsx title="next.config.js" + const stylexPlugin = require('@stylexjs/nextjs-plugin'); -#### Next.js + module.exports = stylexPlugin({ + rootDir: __dirname, + })({}); + ``` - +
Please refer to the [StyleX examples apps](https://github.com/facebookexternal/stylex/tree/main/apps) diff --git a/apps/docs/docs/learn/02-thinking-in-stylex.mdx b/apps/docs/docs/learn/02-thinking-in-stylex.mdx deleted file mode 100755 index 8ed882ff..00000000 --- a/apps/docs/docs/learn/02-thinking-in-stylex.mdx +++ /dev/null @@ -1,470 +0,0 @@ ---- -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. -sidebar_position: 2 ---- - -# Thinking in StyleX - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Core Principles - -To understand why StyleX exists and the reasoning behind its decisions, it may be beneficial to familiarize oneself with the fundamental principles that guide it. This may help you decide if StyleX is the right solution for you. - -These principles should also be helpful when designing new APIs for StyleX. - -### Co-location - -There are benefits to making your code DRY, but we don't think that's true when it comes to writing your styles. - -The best and most readable way to write your styles is to write them in the same -file as the markup where you use them. Repeating yourself is not a bad thing -when it comes to styles. - -StyleX is designed to let you author, apply and reason about your styles -locally. - -### Deterministic resolution - -CSS is a powerful and expressive language. However, it can sometimes -feel fragile. Some of this stems from a misunderstanding of how CSS works, but a -lot of it stems from the discipline and organization required to keep your CSS -selectors with different specificities from stepping on each other's toes. - -Most existing solutions to this problem rely on rules and conventions. - -
- BEM and OOCSS Conventions - BEM and OOCSS introduce naming conventions to avoid these problems relying on - developers to consistently follow the rules, frequently avoiding merging styles at all. - This can lead to bloated CSS. -
- -
- Utility Classes - Atomic utility class names like Tailwind CSS and Tachyons rely on a convention to ensure you don't - apply conflicting class names on the same element. The tooling to enforce this rule relies on all - your styles being present inline in the same string. This makes it harder to refactor your styles - into separate groups and merge them within the same file or across file boundaries. -
- -StyleX aims to improve on both the consistency and predictability of styles -_and_ the expressive power available. We believe this is possible through -build-tools. - -StyleX provides a completely predictable and deterministic styling -system that works across files. It produces deterministic results not only when merging multiple selectors, but also when merging multiple shorthand and longhand properties. (e.g. `margin` vs `margin-top`). - -> "The last style applied always wins." - -### Low-cost Abstractions - -When it comes to the performance cost of StyleX, our guiding principle is that -StyleX should always be the fastest way to achieve a particular pattern. -Unless you opt in to a feature, you should never pay for it with runtime -performance. We make the trade-off of doing more work at build-time to improve -runtime performance. - -Here's how this plays out in practice: - -#### 1. Styles created and applied locally - -When authoring and consuming styles within the same file, the cost of StyleX is zero. This is because in addition to compiling away your `stylex.create` calls, StyleX also compiles away your `stylex.props` calls when possible. - -So, - -```tsx -import * as stylex from 'stylex'; -const styles = stylex.create({ - red: {color: 'red'}, -}); -let a = stylex.props(styles.red); -``` - -Compiles down to: - - - - -```tsx -import * as stylex from 'stylex'; - -let a = {className: 'x1e2nbdu'}; -``` - - - - -```css -.x1e2nbdu { - color: red; -} -``` - - - - -There is no runtime overhead here. - -#### 2. Using styles across files - -When passing styles across file boundaries, you pay a small cost for the -additional power and expressivity. The `stylex.create` call is not deleted -entirely and instead leaves behind an object mapping keys to -class names. And the `stylex()` or `stylex.props()` calls are executed at -runtime. - -This code, for example: - -```tsx -import * as stylex from '@stylexjs/stylex'; - -const styles = stylex.create({ - foo: { - color: 'red', - }, - bar: { - backgroundColor: 'blue', - }, -}); - -function MyComponent({xstyle}) { - return
; -} -``` - -Compiles down to: - -```tsx -import * as stylex from '@stylexjs/stylex'; - -const styles = stylex.create({ - foo: { - color: 'x1e2nbdu', - $$css: true, - }, - bar: { - backgroundColor: 'x1t391ir', - $$css: true, - }, -}); - -function MyComponent((xstyle)) { - return
; -} -``` - -This definitely a little more code, but the runtime cost is still minimal -because of how fast the `stylex()` and `stylex.props()` functions are. - -Most other styling solutions don't enable composition of styles across file -boundaries. The state of the art is to combine lists of class names. - -### Small API Surface - -Our goal is to make StyleX as minimal and easy-to-learn as possible. As such we -don't want to invent too many APIs. Instead, we want to be able to lean on -common JavaScript patterns where possible and give you the smallest API surface -possible. - -At its core StyleX can be boiled down to two functions: - -1. `stylex.create` -2. `stylex.props` - -You use `stylex.create` to create a bunch of styles and then you use -`stylex.props` to apply those styles to an element. - -Within these two functions, we choose to rely on common JS patterns rather than -introduce unique API or patterns for StyleX. e.g. we don't have an API for -conditional styles. Instead we let you apply styles conditionally with -boolean expression or ternary expressions. - -Things work the way you expect when dealing with JavaScript objects and arrays. -There are no surprises. - -### Type-Safe Styles - -Typescript has becomes massively popular due to the experience and safety it -provides. Our styles, however, have largly remained untyped and unreliable. -Except for some path-breaking projects such as -[Vanilla Extract](https://vanilla-extract.style/), styles are just bags of -strings in most styling solutions. - -StyleX is authored in Flow with strong static types. It's packages on NPM come -with auto-generated types for both Flow and TypeScript. When there are -incompatibilies between the two type-systems we take the time to ensure that we -write custom Typescript types to achieve the same level of power and safety as -the original Flow. - -_All styles are typed_. When accepting styles as props, you can use types to -constrain what styles you will accept. Styles should be as type-safe as -any other props for your components. - -The StyleX API is strongly typed. Every style you author has types as well. This is -only really possible because we use JavaScript objects to author `RawStyles`. - -These types can then be leveraged to set contracts for the styles that your -component will accept You may make a component that only accepts -`color` and `backgroundColor` but no other styles. - -```ts -import type {StyleXStyles} from '@stylexjs/stylex'; - -type Props = { - //... - style?: StyleXStyles<{color?: string; backgroundColor?: string}>; - //... -}; -``` - -In another example, you may disallow margins while allowing any other styles: - -```ts -import type {StyleXStylesWithout} from '@stylexjs/stylex'; - -type Props = { - //... - style?: StyleXStylesWithout<{ - margin: unknown; - marginBlock: unknown; - marginInline: unknown; - marginTop: unknown; - marginBottom: unknown; - marginLeft: unknown; - marginRight: unknown; - marginBlockStart: unknown; - marginBlockEnd: unknown; - marginInlineStart: unknown; - marginInlineEnd: unknown; - }>; - //... -}; -``` - -Your styles being typed allows you to set up extremely sophisticated rules -about the ways in which a component styles can be customized with **zero-runtime -cost**. - -**Strings don't have Types**. By using JavaScript objects for styles, we retain -type information when you define styles. - -### Shareable constants - -CSS class names, CSS variables and other CSS identifiers are defined in a global -namespace. The obvious and common API to bring them into JavaScript is as -strings. However, this means losing type-safety and composability. - -We want styles to be type-safe and so we've spent a lot of time coming up with -APIs to replace these magic strings with references to Javacript constants. So -far this is reflected in the following APIs: - -1. `stylex.create` Abstracts away the generated class names entirely. You deal - with "opaque" JavaScript objects with strong types to indicate the styles - they represent. -2. `stylex.defineVars` Abstracts away the names of CSS variables generated. You - import and use them as constants in your styles directly. The type of values - these variables represent is maintained in the typesystem. -3. `stylex.keyframes` Abstracts away the names of keyframe animations. Instead - you declare them as constants and use them by reference. - -We're looking into ways to make other CSS identifiers such as -`container-name` and `@font-face` type-safe as well. - -### Framework-agnostic - -StyleX is a CSS-in-JS solution, not a CSS-in-React solution. Although StyleX -been tailored to works best with React today, It's designed to be used with any -JavaScript framework that lets you author your markup in your Javacript. - -`stylex.props` returns an object with `className` and `style` properties. You -may need to use a wrapper function to convert this to make it work with your -framework of choice. - -### Encapsulation - -> All styles on an element should be caused by class names on that element -> itself. - -CSS makes it very easy to author styles in a way that let you cause "styles at a -distance": - -- `.className > *` -- `.className ~ *` -- `.className:hover > div:first-child` - -All of these patterns, while powerful, makes your styles fragile and harder to -debug. You apply class names on one element and affect a completely different -element. - -Inheritable styles such as `color` will still be inherited, but that is the -_only_ form of style-at-a-distance that StyleX allows. And in those cases too, -the styles applied directly on an element always take precedence over inherited -styles. - -This is often not the case when using complex selectors as the complex selectors -usually have higher specificity than the simple classname selectors used for -styles applied directly on the element. - -StyleX disallows this entire class of selectors. This currently makes some -patterns in CSS impossible: - -- e.g. It is currently impossible for a child element to have conditional styles - when it's parent is hovered. - -Our goal is to support these patterns without sacrificing style encapsulation. - -StyleX is not CSS pre-processor. It intentionally puts constraints on the power -of CSS selectors in order to give you a fast and predictable system. - -Using a JavaScript object as an API transparently nudges you into writing valid -styles, as writing complex selectors _feels_ out of place. - -An API that allows template strings signals a freedom where developers -would naturally start writing arbitrary CSS and expect it to _just work_. Such -expectations are incompatible with the way StyleX actually works. In order to -mitigate this issue, one would need to do aggressive validation with ESlint and -Babel. - -This would lead to an unpleasant experience where the API guides you in one -direction while the tooling gets noisy and tries to keep you within the bounds. - -### Readability & Maintainabity over Terseness - -Many recent styling solutions are extremely terse and easy to write. Tailwind -CSS is popular and lets you author your styles by putting extremely terse -utility class names directly on your markup. This is great for authoring styles -quickly, but in our opinion, in the long run, it makes the styles harder to read -and maintain. It can also have a steeper learning curve up-front. - -StyleX makes the choice to use the same CSS property names that you're already -used to. _(We did decide to use camelCase instead of kebab-case as that makes -them easier to type in JavaScript.)_ This makes it easier to learn and read. - -We also enforce that your styles are authored in objects separate from the HTML -elements where you use them. We made this decision to help with the readabiliy -of your HTML markup and so you can choose appropriate names for your styles to -indicate their purpose. `styles.active` tells you much more about the purpose of -a bunch of styles than what those styles are themselves. - -This principle leads to trade-offs where authoring styles may take more typing -with StyleX than some other solutions. You're also forced to name your style -objects when styling new elements. - -We believe these costs are worth the improved readability over time. Giving each -HTML element a semantic name can communicate a lot more than the styles -themselves. - -:::info - -One side benefit of using references to styles rather than using the styles -inline is **testability**. In a unit-testing environment, StyleX can be -configured to remove all atomic styles and only output single debugging -class names to show you the source of the styles applied to an element. - -Among other benefits, it makes snapshot tests more resilient as they won't -change for every style change. - -::: - -### Modularity and Composability - -NPM has made it extremely easy to share code across projects. However, sharing -CSS has remained a challenge. - -When using third-party components, you generally have to either choose between -components with styles baked in that are hard or impossible to customize, or -"headless" components that are completely unstyled. - -The lack of a good system to predictably merge and compose styles across -packages has also been an obstacle when sharing styles within packages. - -StyleX aims to create a system to easily and reliably share styles along with -components within packages on NPM. - -### Avoiding Global Configuration - -In order to make styles modular and composable, we worked to avoid -configuration where possible. StyleX should work with no configuration out of -the box which should ensure that styles from different packages don't conflict -with each other. - -Many other styling solutions offer a way to create project-specific -configurations. You're able to define common tokens, breakpoints and other -shorthand in such configurations. We have chosen to eschew such configurations -to prioritise composability over convenience. - -In order to provide some of the benefits of such global configurations, we allow -declaring styles and variables in modules that you can import and use. We're -also working on configurable ESlint rules that will let you enforce -project-specific style conventions without changing the behaviour of StyleX -itself. - -### One small file over many smaller files - -Most styling solutions lead to CSS that grows linearly with the size of your -codebase. For larger applications it makes loading all your CSS upfront -infeasible. So you end up splitting your CSS into multiple files and loading -them lazily as you need them. - -This improves your initial load time and performance, but it comes at the cost -of slower updates as the browser needs to recalculate styles every time more CSS -is loaded. - -With StyleX, we're aiming to create a system where the total amount of CSS is -small-enough that you can load all your CSS upfront without a significant -performance penalty. - -The StyleX model doesn't make lazy-loading or critical-css impossible, but we -are not optimizing for it for now and are focusing on generating smallest, -fastest single CSS bundle possible. - -## Ideal conditions for using StyleX? - -StyleX works well in a wide variety of projects, and you will usually not regret -choosing it. However, there are some conditions that are more ideal for using StyleX than -others. - -### When authoring your UI in JavaScript - -StyleX is a CSS-in-JS library, which means that it is most useful when you are -authoring your UI in JavaScript. If you're using a framework such as React, -Preact, Solid, lit-html, or Angular, using StyleX should be a breeze. - -Some frameworks, such as Svelte and Vue use custom file formats that are -compiled to JavaScript at build time. StyleX can still be used in these -frameworks, but may need some extra configuration. - -There are also UI solutions such as `htmx` and various templating engines where -you author your markup in files that can't contain arbitrary JavaScript. Even if -you can make StyleX work in these environments, it may not be worth the effort. - -### Large or growing projects - -While StyleX works well for projects of all sizes, it really shines in larger -applications. - -Since StyleX compiles to atomic class names, a big performance benefit is that -the size of your CSS bundle plateaus as your project grows. - -For smaller projects, it may still make sense to choose StyleX for its -expressive API, but the performance benefits may not be as meaningful. - -### Reusable components - -StyleX makes the most sense when being used alongside reusable UI components. - -For years, we have had to choose between "Design System" components that come -with styles baked in but can be difficult to customize or "Headless" components -that are completely unstyled. - -StyleX finally lets you build UI components that can have default styles _and_ -still be customizable. - -Further, the consistency enables sharing these components by publishing them to -NPM. As long as the consumer of your component is also using StyleX, the styles -will be merged and composed correctly without any additional configuration. diff --git a/apps/docs/docs/learn/03-quick-start.mdx b/apps/docs/docs/learn/03-quick-start.mdx new file mode 100755 index 00000000..503da0b9 --- /dev/null +++ b/apps/docs/docs/learn/03-quick-start.mdx @@ -0,0 +1,143 @@ +--- +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. +sidebar_position: 2 +--- + +# Quick start + +### Defining styles + +Styles are defined using an object syntax and the `create()` API. + +```tsx +import * as stylex from '@stylexjs/stylex'; + +const styles = stylex.create({ + root: { + width: '100%', + maxWidth: 800, + minHeight: 40, + }, +}); +``` + +Any number of rules can be created by using additional keys and additional +calls to `create()`: + +```tsx +import * as stylex from '@stylexjs/stylex'; + +const styles = stylex.create({ + root: { + width: '100%', + maxWidth: 800, + minHeight: 40, + }, + child: { + backgroundColor: 'black', + marginBlock: '1rem', + }, +}); + +const colorStyles = stylex.create({ + red: { + backgroundColor: 'lightred', + borderColor: 'darkred', + }, + green: { + backgroundColor: 'lightgreen', + borderColor: 'darkgreen', + }, +}); +``` + +### Using styles + +To use styles they must be passed to the `props()` function. Styles can be +combined using an array, and applied conditionally using standard JavaScript +expressions. + +```tsx +import * as React from 'react'; +import * as stylex from '@stylexjs/stylex'; + +const styles = stylex.create({ ... }); + +function ReactDiv({ color, isActive, style }) { + return
; +} +``` + +The example above uses JSX. StyleX itself is framework agnostic. The same code +works with other frameworks that accept `className` strings and `style` objects +such as `SolidJS`, `Preact` or `Qwik`. + +For other frameworks, it's easy to write simple helpers functions to convert the +output of `props()` to the appropriate format. For example, here is how you +would use StyleX with an `html` template literal in a component built with +Enhance: + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + +```tsx +import type {StyleXStyles} from '@stylexjs/stylex'; +import * as stylex from '@stylexjs/stylex'; + +import { spread } from './helpers'; + +const styles = create({ ... }); + +export default function EnhanceDiv({ html, state }) { + const { attrs } = state; + const { color, isActive = false, style } = state; + // Enhance only accepts string attributes. + const stylesObj = style ? JSON.parse(style) : null; + + return html` +
+ +
+ `; +} +``` + +
+ + +```tsx title="helpers.js" +const styleStr = (style) => + Object.entries(style) + .map(([key, value]) => `${key}:${value}`) + .join(';'); + +export const spread = ({className, style}) => + `class="${className}" style="${styleStr(style)}"`; +``` + + + +
diff --git a/apps/docs/docs/learn/04-thinking-in-stylex.mdx b/apps/docs/docs/learn/04-thinking-in-stylex.mdx new file mode 100755 index 00000000..71a4f96e --- /dev/null +++ b/apps/docs/docs/learn/04-thinking-in-stylex.mdx @@ -0,0 +1,390 @@ +--- +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. +sidebar_position: 3 +--- + +# Thinking in StyleX + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Core Principles + +To understand why StyleX exists and the reasoning behind its decisions, it may be +beneficial to familiarize oneself with the fundamental principles that guide it. +This may help you decide if StyleX is the right solution for you. + +These principles should also be helpful when designing new APIs for StyleX. + +### Co-location + +There are benefits of DRY code, but we don't think that's usually true when it +comes to authoring styles. The best and most readable way to write styles is to +write them in the same file as the markup. + +StyleX is designed to for authoring, applying and reasoning about styles locally. + +### Deterministic resolution + +CSS is a powerful and expressive language. However, it can sometimes +feel fragile. Some of this stems from a misunderstanding of how CSS works, but a +lot of it stems from the discipline and organization required to keep CSS +selectors with different specificities from conflicting. + +Most existing solutions to this problem rely on rules and conventions. + +
+ BEM and OOCSS Conventions + BEM and OOCSS introduce naming conventions to avoid these problems relying on + developers to consistently follow the rules, frequently avoiding merging styles at all. + This can lead to bloated CSS. +
+ +
+ Utility Classes + Atomic utility class names like Tailwind CSS and Tachyons rely on a conventions and lint rules + to ensure that conflicting class names are not applied on the same element. Such tooling adds + constraints on where and how styles can be applied putting architectural limitations on styling. +
+ +StyleX aims to improve on both the consistency and predictability of styles +_and_ the expressive power available. We believe this is possible through +build-tools. + +StyleX provides a completely predictable and deterministic styling +system that works across files. It produces deterministic results not only when +merging multiple selectors, but also when merging multiple shorthand and longhand +properties. (e.g. `margin` vs `margin-top`). + +> "The last style applied always wins." + +### Low-cost Abstractions + +When it comes to the performance cost of StyleX, our guiding principle is that +StyleX should always be the fastest way to achieve a particular pattern. +Common patterns should have no runtime cost and advanced patterns should be +as fast as possible. We make the trade-off of doing more work at build-time to improve +runtime performance. + +Here's how this plays out in practice: + +#### 1. Styles created and applied locally + +When authoring and consuming styles within the same file, the cost of StyleX is zero. +This is because in addition to compiling away `stylex.create` calls, StyleX also +compiles away `stylex.props` calls when possible. + +So, + +```tsx +import * as stylex from 'stylex'; +const styles = stylex.create({ + red: {color: 'red'}, +}); +let a = stylex.props(styles.red); +``` + +Compiles down to: + + + + +```tsx +import * as stylex from 'stylex'; + +let a = {className: 'x1e2nbdu'}; +``` + + + + +```css +.x1e2nbdu { + color: red; +} +``` + + + + +There is no runtime overhead here. + +#### 2. Using styles across files + +Passing styles across file boundaries incurs a small cost for the +additional power and expressivity. The `stylex.create` call is not deleted +entirely and instead leaves behind an object mapping keys to +class names. And the `stylex.props()` calls are executed at runtime. + +This code, for example: + +```tsx +import * as stylex from '@stylexjs/stylex'; + +const styles = stylex.create({ + foo: { + color: 'red', + }, + bar: { + backgroundColor: 'blue', + }, +}); + +function MyComponent({xstyle}) { + return
; +} +``` + +Compiles down to: + +```tsx +import * as stylex from '@stylexjs/stylex'; + +const styles = stylex.create({ + foo: { + color: 'x1e2nbdu', + $$css: true, + }, + bar: { + backgroundColor: 'x1t391ir', + $$css: true, + }, +}); + +function MyComponent((xstyle)) { + return
; +} +``` + +This definitely a little more code, but the runtime cost is still minimal +because of how fast the `stylex()` and `stylex.props()` functions are. + +Most other styling solutions don't enable composition of styles across file +boundaries. The state of the art is to combine lists of class names. + +### Small API Surface + +Our goal is to make StyleX as minimal and easy-to-learn as possible. As such we +don't want to invent too many APIs. Instead, we want to be able to lean on +common JavaScript patterns where possible and provide the smallest API surface +possible. + +At its core StyleX can be boiled down to two functions: + +1. `stylex.create` +2. `stylex.props` + +`stylex.create` is used to create styles and `stylex.props` is used to apply +those styles to an element. + +Within these two functions, we choose to rely on common JS patterns rather than +introduce unique API or patterns for StyleX. e.g. we don't have an API for +conditional styles. Instead we support applying styles conditionally with +boolean or ternary expressions. + +Things should work as expected when dealing with JavaScript objects and arrays. +There should be no surprises. + +### Type-Safe Styles + +Typescript has becomes massively popular due to the experience and safety it +provides. Our styles, however, have largly remained untyped and unreliable. +Except for some path-breaking projects such as +[Vanilla Extract](https://vanilla-extract.style/), styles are just bags of +strings in most styling solutions. + +StyleX is authored in Flow with strong static types. It's packages on NPM come +with auto-generated types for both Flow and TypeScript. When there are +incompatibilies between the two type-systems we take the time to ensure that we +write custom Typescript types to achieve at least the same level of power and safety +as the original Flow. + +_All styles are typed_. When accepting styles as props, types can be used to +constrain what styles are accepted. Styles should be as type-safe as +any other component props. + +The StyleX API is strongly typed. The styles defiend with StyleX are typed too. +This is made possible by using JavaScript objects to author raw styles. This is +one of the big reasons we have chosen objects over template strings. + +These types can then be leveraged to set contracts for the styles that a +component will accept. For example, a component props can defined to only accept +`color` and `backgroundColor` but no other styles. + +```ts +import type {StyleXStyles} from '@stylexjs/stylex'; + +type Props = { + //... + style?: StyleXStyles<{color?: string; backgroundColor?: string}>; + //... +}; +``` + +In another example, the props may disallow margins while allowing all other styles. + +```ts +import type {StyleXStylesWithout} from '@stylexjs/stylex'; + +type Props = { + //... + style?: StyleXStylesWithout<{ + margin: unknown; + marginBlock: unknown; + marginInline: unknown; + marginTop: unknown; + marginBottom: unknown; + marginLeft: unknown; + marginRight: unknown; + marginBlockStart: unknown; + marginBlockEnd: unknown; + marginInlineStart: unknown; + marginInlineEnd: unknown; + }>; + //... +}; +``` + +Styles being typed enables extremely sophisticated rules about the ways in which +a component styles can be customized with **zero-runtime cost**. + +### Shareable constants + +CSS class names, CSS variables and other CSS identifiers are defined in a global +namespace. The obvious and common API to bring them into JavaScript is as +strings. However, this means losing type-safety and composability. + +We want styles to be type-safe and so we've spent a lot of time coming up with +APIs to replace these magic strings with references to Javacript constants. So +far this is reflected in the following APIs: + +1. `stylex.create` Abstracts away the generated class names entirely. You deal + with "opaque" JavaScript objects with strong types to indicate the styles + they represent. +2. `stylex.defineVars` Abstracts away the names of CSS variables generated. + They can be imported as constants and used within styles directly. +3. `stylex.keyframes` Abstracts away the names of keyframe animations. Instead + they are declared as constants and used by reference. + +We're looking into ways to make other CSS identifiers such as +`container-name` and `@font-face` type-safe as well. + +### Framework-agnostic + +StyleX is a CSS-in-JS solution, not a CSS-in-React solution. Although StyleX +been tailored to works best with React today, It's designed to be used with any +JavaScript framework that allows authoring markup in Javacript. This includes frameworks +that use JSX, template strings, etc. + +`stylex.props` returns an object with `className` and `style` properties. A wrapper +function may be needed to convert this to make it work with various frameworks. + +### Encapsulation + +> All styles on an element should be caused by class names on that element +> itself. + +CSS makes it very easy to author styles in a way that can cause "styles at a +distance": + +- `.className > *` +- `.className ~ *` +- `.className:hover > div:first-child` + +All of these patterns, while powerful, makes styles fragile and less predictable. +Applying class names on one element and can affect a completely different element. + +Inheritable styles such as `color` will still be inherited, but that is the +_only_ form of style-at-a-distance that StyleX allows. And in those cases too, +the styles applied directly on an element always take precedence over inherited +styles. + +This is often not the case when using complex selectors as the complex selectors +usually have higher specificity than the simple classname selectors used for +styles applied directly on the element. + +StyleX disallows this entire class of selectors. This currently makes certain CSS +patterns impossible to achieve with StyleX. Our goal is to support these patterns +without sacrificing style encapsulation. + +StyleX is not CSS pre-processor. It intentionally puts constraints on the power +of CSS selectors in order to build a fast and predictable system. The API, based +on Javascript objects instead of template strings, is designed to make these +constraints feel natural. + +### Readability & Maintainabity over Terseness + +Some recent utility-based styling solutions are extremely terse and easy to write. +StyleX chooses to prioritize readability and maintainability over terseness. + +StyleX makes the choice to use familiar CSS property names to prioritize readability +and a shallow learning curve. _(We did decide to use camelCase instead of kebab-case +for convenience.)_ + +We also enforce that styles are authored in objects separate from the HTML +elements where they are used. We made this decision to help with the readabiliy +of HTML markup and so appropriate names for styles to indicate their purpose. +E.g. Using a name `styles.active` like emphasizes *why* styles are being applied +without having to dig through *what* styles are being applied. + +This principle leads to trade-offs where authoring styles may take more typing +with StyleX than some other solutions. + +We believe these costs are worth the improved readability over time. Giving each +HTML element a semantic name can communicate a lot more than the styles +themselves. + +:::info + +One side benefit of using references to styles rather than using the styles +inline is **testability**. In a unit-testing environment, StyleX can be +configured to remove all atomic styles and only output single debugging +class names to indicate the source location of styles rather than the actual +styles. + +Among other benefits, it makes snapshot tests more resilient as they won't +change for every style change. + +::: + +### Modularity and Composability + +NPM has made it extremely easy to share code across projects. However, sharing +CSS has remained a challenge. Third-party components, either have styles baked +in that are hard or impossible to customize, or are completely unstyled. + +The lack of a good system to predictably merge and compose styles across +packages has also been an obstacle when sharing styles within packages. + +StyleX aims to create a system to easily and reliably share styles along with +components within packages on NPM. + +### Avoid Global Configuration + +StyleX should work similarly across projects. Creating preoject-specific +configurations that change the syntax or behaviour of StyleX should be avoided. +We have chosen to prioritise composability and consistency over short-term +convenience. + +We lean on tooling to provide convenience and customizability. For example, the +StyleX ESlint rule can be configured to allow or disallow certain CSS properties +within a project. This can be used in concert with static types to create +styling rules for a project. + +### One small file over many smaller files + +When dealing with a large amount CSS, lazy loading CSS is a way to speed up +the initial load time of a page. However, it comes at the cost of a slower +update times, or the *Interation to Next Paint (INP)* metric. Lazy loading +any CSS on a page triggers a recalculation of styles for the entire page. + +StyleX is optimized for generating a single, highly optimized, CSS bundle that +is loaded upfront. Our is to create a system where the total amount of CSS is +small-enough that all the CSS can be loaded upfront without a noticeable +performance impact. + +Other techniques to make the initial load times faster, such as "critical CSS" +are compatible with StyleX, but should usually be unnecessary. diff --git a/apps/docs/docs/learn/03-styling-ui/01-defining-styles.mdx b/apps/docs/docs/learn/05-styling-ui/01-defining-styles.mdx similarity index 100% rename from apps/docs/docs/learn/03-styling-ui/01-defining-styles.mdx rename to apps/docs/docs/learn/05-styling-ui/01-defining-styles.mdx diff --git a/apps/docs/docs/learn/03-styling-ui/02-using-styles.mdx b/apps/docs/docs/learn/05-styling-ui/02-using-styles.mdx similarity index 100% rename from apps/docs/docs/learn/03-styling-ui/02-using-styles.mdx rename to apps/docs/docs/learn/05-styling-ui/02-using-styles.mdx diff --git a/apps/docs/docs/learn/03-styling-ui/_category_.json b/apps/docs/docs/learn/05-styling-ui/_category_.json similarity index 100% rename from apps/docs/docs/learn/03-styling-ui/_category_.json rename to apps/docs/docs/learn/05-styling-ui/_category_.json diff --git a/apps/docs/docs/learn/04-theming/01-defining-variables.mdx b/apps/docs/docs/learn/06-theming/01-defining-variables.mdx similarity index 100% rename from apps/docs/docs/learn/04-theming/01-defining-variables.mdx rename to apps/docs/docs/learn/06-theming/01-defining-variables.mdx diff --git a/apps/docs/docs/learn/04-theming/02-using-variables.mdx b/apps/docs/docs/learn/06-theming/02-using-variables.mdx similarity index 100% rename from apps/docs/docs/learn/04-theming/02-using-variables.mdx rename to apps/docs/docs/learn/06-theming/02-using-variables.mdx diff --git a/apps/docs/docs/learn/04-theming/03-creating-themes.mdx b/apps/docs/docs/learn/06-theming/03-creating-themes.mdx similarity index 100% rename from apps/docs/docs/learn/04-theming/03-creating-themes.mdx rename to apps/docs/docs/learn/06-theming/03-creating-themes.mdx diff --git a/apps/docs/docs/learn/04-theming/_category_.json b/apps/docs/docs/learn/06-theming/_category_.json similarity index 100% rename from apps/docs/docs/learn/04-theming/_category_.json rename to apps/docs/docs/learn/06-theming/_category_.json diff --git a/apps/docs/docs/learn/05-static-types.mdx b/apps/docs/docs/learn/07-static-types.mdx similarity index 100% rename from apps/docs/docs/learn/05-static-types.mdx rename to apps/docs/docs/learn/07-static-types.mdx diff --git a/apps/docs/docs/learn/06-acknowledgements.mdx b/apps/docs/docs/learn/08-acknowledgements.mdx similarity index 100% rename from apps/docs/docs/learn/06-acknowledgements.mdx rename to apps/docs/docs/learn/08-acknowledgements.mdx diff --git a/apps/docs/docs/learn/index.mdx b/apps/docs/docs/learn/index.mdx old mode 100755 new mode 100644 index 408ace94..a5574844 --- a/apps/docs/docs/learn/index.mdx +++ b/apps/docs/docs/learn/index.mdx @@ -4,19 +4,26 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. sidebar_position: 0 +title: Introduction --- -# Quick start +# Introduction to StyleX StyleX is a simple, easy to use JavaScript syntax and compiler for styling web apps. +StyleX combines the strengths and avoids the weaknesses of both inline styles +and static CSS. Defining and using styles requires only local knowledge within a +component, and avoids specificity issues while retaining features like Media +Queries. StyleX builds optimized styles using collision-free atomic CSS which is +superior to what could be authored and maintained by hand. + ## Features at a glance ### Predictable -- Style Encapsulation - All styles are caused by class names applied directly set - on an element +- Style Encapsulation - All styles are applied as class names applied directly + on elements - No specificity issues - โ€œThe last style applied always wins!โ€ @@ -35,7 +42,7 @@ apps. ### Static & Fast -- No runtime style injections +- No runtime style injection - All styles are bundled in a static CSS file at compile-time - Optimized runtime for merging class names @@ -46,144 +53,40 @@ apps. - Type-safe theming - Type-safe style props -## StyleX in 5 minutes +## Ideal conditions for using StyleX? -StyleX combines the strengths and avoids the weaknesses of both inline styles -and static CSS. Defining and using styles requires only local knowledge within a -component, and avoids specificity issues while retaining features like Media -Queries. StyleX builds optimized styles using collision-free atomic CSS which is -superior to what could be authored and maintained by hand. +StyleX works well in a wide variety of projects. However, there are some conditions +that are more ideal for using StyleX than others. + +### When authoring your UI in JavaScript + +StyleX is a CSS-in-JS library, which means that it is most useful when an app's UI is +authored in Javascript. If an application uses a framework like React, Preact, Solid, +lit-html, or Angular, using StyleX should be a good fit. + +Some frameworks, such as Svelte and Vue use custom file formats that are +compiled to JavaScript at build time. StyleX can still be used in these +frameworks, but may need some custom configuration. + +### Large or growing projects + +While StyleX works well for projects of all sizes, it really shines in larger +applications. + +Since StyleX compiles to atomic class names, a big performance benefit is that +the size of your CSS bundle plateaus as your project grows. + +### Reusable components + +The benefits of StyleX are greatest when used alongside reusable UI components. + +For years, we have had to choose between "Design System" components that come +with styles baked in but can be difficult to customize or "Headless" components +that are completely unstyled. + +StyleX empowers developers to build UI components that can have default styles _and_ +still be customizable. -### Defining styles - -Styles are defined using an object syntax and the `create()` API. - -```tsx -import * as stylex from '@stylexjs/stylex'; - -const styles = stylex.create({ - root: { - width: '100%', - maxWidth: 800, - minHeight: 40, - }, -}); -``` - -Any number of rules can be created by using additional keys and additional -calls to `create()`: - -```tsx -import * as stylex from '@stylexjs/stylex'; - -const styles = stylex.create({ - root: { - width: '100%', - maxWidth: 800, - minHeight: 40, - }, - child: { - backgroundColor: 'black', - marginBlock: '1rem', - }, -}); - -const colorStyles = stylex.create({ - red: { - backgroundColor: 'lightred', - borderColor: 'darkred', - }, - green: { - backgroundColor: 'lightgreen', - borderColor: 'darkgreen', - }, -}); -``` - -### Using styles - -To use styles they must be passed to the `props()` function. Styles can be -combined using an array, and applied conditionally using standard JavaScript -expressions. - -```tsx -import * as React from 'react'; -import * as stylex from '@stylexjs/stylex'; - -const styles = stylex.create({ ... }); - -function ReactDiv({ color, isActive, style }) { - return
; -} -``` - -The example above uses JSX. StyleX itself is framework agnostic. The same code -works with other frameworks that accept `className` strings and `style` objects -such as `SolidJS`, `Preact` or `Qwik`. - -For other frameworks, it's easy to write simple helpers functions to convert the -output of `props()` to the appropriate format. For example, here is how you -would use StyleX with an `html` template literal in a component built with -Enhance: - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - - -```tsx -import type {StyleXStyles} from '@stylexjs/stylex'; -import * as stylex from '@stylexjs/stylex'; - -import { spread } from './helpers'; - -const styles = create({ ... }); - -export default function EnhanceDiv({ html, state }) { - const { attrs } = state; - const { color, isActive = false, style } = state; - // Enhance only accepts string attributes. - const stylesObj = style ? JSON.parse(style) : null; - - return html` -
- -
- `; -} -``` - -
- - -```tsx title="helpers.js" -const styleStr = (style) => - Object.entries(style) - .map(([key, value]) => `${key}:${value}`) - .join(';'); - -export const spread = ({className, style}) => - `class="${className}" style="${styleStr(style)}"`; -``` - - - -
+Further, the consistency enables sharing these components by publishing them to +NPM. As long as the consumer of a component is also using StyleX, the styles +will be merged and composed correctly without any additional configuration. \ No newline at end of file diff --git a/apps/docs/scripts/make-stylex-sheet.js b/apps/docs/scripts/make-stylex-sheet.js index d5ab364f..210270bf 100644 --- a/apps/docs/scripts/make-stylex-sheet.js +++ b/apps/docs/scripts/make-stylex-sheet.js @@ -29,7 +29,12 @@ async function transformFile(filePath) { { dev: false, test: false, - stylexSheetName: 'custom', + stylexSheetName: '<>', + genConditionalClasses: true, + unstable_moduleResolution: { + type: 'commonJS', + rootDir: path.join(__dirname, '../../..'), + }, }, ], ], @@ -64,10 +69,7 @@ async function genSheet() { path.join(__dirname, '../components'), '.js', ); - const pagesPromise = getAllFilesOfType( - path.join(__dirname, '../src/pages'), - '.js', - ); + const pagesPromise = getAllFilesOfType(path.join(__dirname, '../src'), '.js'); const docsPromise = getAllFilesOfType(path.join(__dirname, '../docs'), '.js'); const components = await componentsPromise; const pages = await pagesPromise; @@ -85,8 +87,6 @@ async function genSheet() { '.css', ); - console.log(generatedCSSPaths); - if (generatedCSSPaths.length !== 1) { console.error( 'Could not find a single CSS file. Instead found', diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 76e2f8c4..ddef4919 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -71,6 +71,9 @@ --pink-s: 62%; --pink-l: 60%; + --ifm-alert-border-color: hsla(202, 100%, 50%, 1); + --ifm-alert-background-color: hsla(202, 100%, 50%, 1); + --ifm-navbar-background-color: var(--bg2); --ifm-footer-background-color: var(--bg1); --ifm-heading-font-family: 'Avenir Next', system-ui, -apple-system, 'Segoe UI', @@ -153,6 +156,9 @@ html[data-theme='dark']:root { --pink-s: 62%; --pink-l: 66%; + --ifm-alert-border-color: hsl(249, 70%, 57%); + --ifm-alert-background-color: hsl(249, 70%, 57%); + --purple-navy: hsla(251, 19%, 39%, 1); --black-coffee: hsla(330, 6%, 20%, 1); --ifm-toc-border-color: hsla(330, 6%, 75%, 0.1); @@ -232,7 +238,7 @@ html[data-theme='dark'] .aa-DetachedSearchButton { .navbar-github-link:after { transition: opacity 0.2s; - content: ""; + content: ''; width: 24px; height: 24px; display: flex; diff --git a/apps/docs/src/pages/index.js b/apps/docs/src/pages/index.js index cae764a5..fd39e98e 100644 --- a/apps/docs/src/pages/index.js +++ b/apps/docs/src/pages/index.js @@ -13,6 +13,7 @@ import Layout from '@theme/Layout'; import StylexAnimatedLogo from '@site/components/StylexAnimatedLogo'; import CodeBlock from '@site/components/CodeBlock'; import Link from '@docusaurus/Link'; +import FeaturePile from '../../components/FeaturePile'; const STEP_CONFIGURE = `import plugin from '@stylexjs/rollup-plugin'; @@ -66,7 +67,9 @@ const CardDescription = ({children}) => ( const CodeContainer = ({children}) => (
- {children} + + {children} +
); @@ -99,33 +102,43 @@ export default function Home() { user-interfaces.
- - Learn โ†’ + + Get Started โ†’ - Get Started โ†’ + to="/docs/learn/thinking-in-stylex/"> + Thinking in StyleX โ†’
-
+
+ +
+

{'Easy as 1, 2, 3'}

- - Step 1 - Configure the compiler - {STEP_CONFIGURE} - - - Step 2 - Create your styles - {STEP_CREATE} - - - Step 3 - Use your styles - {STEP_USE} - +
+ + Step 1 + Configure the compiler + {STEP_CONFIGURE} + + + Step 2 + Create your styles + {STEP_CREATE} + + + Step 3 + Use your styles + {STEP_USE} + +
@@ -152,6 +165,10 @@ const styles = stylex.create({ flexDirection: 'column', alignItems: 'center', justifyContent: 'center', + width: '100%', + }, + heroPadding: { + padding: 32, }, title: { position: 'relative', @@ -266,6 +283,8 @@ const styles = stylex.create({ }, getStarted: { backgroundColor: 'var(--bg2)', + }, + getStartedLayout: { width: '100%', padding: 16, display: 'flex', @@ -310,21 +329,12 @@ const styles = stylex.create({ marginTop: 16, fontFamily: 'ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace', - borderRadius: 16, - fontSize: { - default: '1rem', - '@media (min-width: 1250px) and (max-width: 1500px)': '0.8rem', - '@media (min-width: 420px) and (max-width: 550px)': '0.8rem', - '@media (max-width: 419px)': '0.65rem', - }, + borderRadius: 32, + containerType: 'inline-size', }, code: { flexGrow: 1, - }, - codeEmpty: { - alignItems: 'center', - justifyContent: 'center', - fontSize: '8rem', + fontSize: 'clamp(0.6rem, 0.15rem + 3cqi, 1rem)', }, zstack: { alignItems: { diff --git a/apps/docs/src/theme/MDXComponents/Details.js b/apps/docs/src/theme/MDXComponents/Details.js new file mode 100644 index 00000000..fb0a2b94 --- /dev/null +++ b/apps/docs/src/theme/MDXComponents/Details.js @@ -0,0 +1,87 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import * as stylex from '@stylexjs/stylex'; +import {tokens} from './DetailsTokens.stylex'; + +export default function MDXDetails({children: _children, style, ...props}) { + const items = React.Children.toArray(_children); + // Split summary item from the rest to pass it as a separate prop to the + // Details theme component + const summary = items.find( + (item) => React.isValidElement(item) && item.props?.mdxType === 'summary', + ); + const children = <>{items.filter((item) => item !== summary)}; + + return ( +
+ + {children} +
+ ); +} + +const RETINA = + '@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)'; + +const styles = stylex.create({ + details: { + display: 'grid', + gridTemplateRows: 'auto 1fr', + borderRadius: 8, + padding: '1rem', + marginBottom: '1rem', + backgroundColor: 'hsla(var(--cyan-h), var(--cyan-s), var(--cyan-l), 0.1)', + borderWidth: { + default: 1, + [RETINA]: 0.5, + }, + borderStyle: 'solid', + borderColor: 'hsla(var(--cyan-h), var(--cyan-s), var(--cyan-l), 0.5)', + [tokens.arrowRotate]: { + default: '0deg', + ':is([open])': '90deg', + }, + [tokens.summaryGap]: { + default: '0rem', + ':is([open])': '1rem', + }, + }, + summary: { + cursor: 'pointer', + fontWeight: 'bold', + listStyleType: 'none', + marginBottom: tokens.summaryGap, + paddingInlineStart: '1.2rem', + position: 'relative', + '::-webkit-details-marker': { + display: 'none', + }, + '::marker': { + display: 'none', + }, + '::before': { + content: '', + marginRight: '0.5rem', + borderWidth: '.4rem', + borderStyle: 'solid', + borderColor: 'transparent', + borderInlineStartColor: 'var(--pink)', + position: 'absolute', + top: '0.5rem', + insetInlineStart: '0.25rem', + transform: `rotate(${tokens.arrowRotate})`, + transformOrigin: '0.2rem 50%', + transitionProperty: 'transform', + transitionDuration: '0.2s', + transitionTimingFunction: 'ease-in-out', + }, + }, +}); diff --git a/apps/docs/src/theme/MDXComponents/DetailsTokens.stylex.js b/apps/docs/src/theme/MDXComponents/DetailsTokens.stylex.js new file mode 100644 index 00000000..6e49f21f --- /dev/null +++ b/apps/docs/src/theme/MDXComponents/DetailsTokens.stylex.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import * as stylex from '@stylexjs/stylex'; + +export const tokens = stylex.defineVars({ + arrowRotate: '0deg', + summaryGap: '0rem', +}); diff --git a/packages/babel-plugin/src/index.js b/packages/babel-plugin/src/index.js index 7d9d3de1..4c1b6e55 100644 --- a/packages/babel-plugin/src/index.js +++ b/packages/babel-plugin/src/index.js @@ -285,7 +285,9 @@ function addSpecificityLevel(selector: string, index: number): string { .map(() => ':not(#\\#)') .join(''); - const lastOpenCurly = selector.lastIndexOf('{'); + const lastOpenCurly = selector.includes('::') + ? selector.indexOf('::') + : selector.lastIndexOf('{'); const beforeCurly = selector.slice(0, lastOpenCurly); const afterCurly = selector.slice(lastOpenCurly);