callstack / callstack/linaria

`fragment` tag for composition

Open
#244 35 comments 65 reactions 1 assignee Claimed by @jayu View on GitHub
core team 🧬 feature: proposal 💬
Dominant language
TypeScript
Stars
12.4k
Forks
413
PR merge metrics
No merged PRs in 30d

Description

## Problem

Currently it's not possible to compose styles from multiple classnames together. Previously we had a `include` helper which would take a class name and return the unprocessed CSS text. I removed it because it wasn't strightforward to implement it with the new approach and I haven't needed to use it personally. In addition, an extra class name was generated whether you used it anywhere or not.

Another usecase is that once you get into an interpolation inside the tagged template, we don't parse the nested interpolations. So it's not possible to do something like following:

```js
const Box = styled.div`
font-size: 14px;

${borders
.map(({ p, w }) => `border-${p}: ${w}px solid ${props => props.color}`)
.join('\n')};
`;
```

`styled-components` has exports `css` tag to support this. However, the `css` tag is used for class names in Linaria, so we can't do the same.

## Prior art

Emotion allows you to interpolate a class name which inlines its styles: https://emotion.sh/docs/composition

One problem with this approach is that you're interpolating a class name, but getting the CSS text instead. This breaks the following use case where you might want to refer to another class name:

```js
const paragraph = css`
font-size: 14px;
`;

const article = css`
backgrounc-color: white;

.${paragraph} {
font-size: 16px;
}
`;
```

## Current solution

Currently, you could do something like this:

```js
const shared = `
font-size: 18px;
`;

const Title = styled.h1`
${shared};

color: black;
`;
```

There are 2 issues with this approach:

1. There's no syntax highlighting or autocomplete for the CSS
2. It's not possibe to use dynamic interpolations like you would in `styled`

## Proposal

We could add a new `fragment` tag to allow this use case:

```js
const shared = fragment.css`
font-size: 18px;
`;

const Title = styled.h1`
${shared};

color: black;
`;
```

The `fragment` tag itself won't generate any class names, but when the result is interpolated, it will inline the CSS text along with the dynamic interpolations. It'll work very similarly to how `styled-components` handles interpolation of `css` tagged template literals. As for parsing, it would be parsed similar to how the `styled.x` tags are parsed.

*Note: Initially I thought of having just `fragment`, but went for `fragment.css` since it allows us to have syntax highlighting and autocomplete.*

Since the `fragment` tag won't generate a class name, to use it as a class, you could do something like this:

```js
const title = css`${shared}`;
```

You could even inline it if you don't like creating an extra variable:

```js


```

## Why not add it

It requires extra code, especially if we want to support interpolations and don't want to leave the CSS code for the fragment inside the JS bundle.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.