Skip to content

Commit

Permalink
🎩 Initial commit of stylish-preact CSS library
Browse files Browse the repository at this point in the history
  • Loading branch information
canterberry committed Dec 6, 2021
0 parents commit d499702
Show file tree
Hide file tree
Showing 9 changed files with 6,404 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Generated by `npm install`
/node_modules/

# Generated by `npm run build`
/lib/
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright 2021 Twuni

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
235 changes: 235 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# 🎩 Stylish | Preact

Stylish is a tiny yet capable CSS library for [Preact][4] apps. It is heavily
inspired by [@emotion/styled][1] and [styled-components][2], so if you are
familiar with either of those, you should quickly get the hang of Stylish.

## Features

* Extremely lightweight (under **1KB** gzipped, _minimal dependencies_)
* Animations supported
* Theme context provided right out of the box
* CSS rules have access to component props
* Auto-generated class names (never have to use a `className` or `style` prop again!)
* Make any component stylish
* No React shims needed -- built for Preact

## Installing

```
npm install stylish-preact
```

Yarn users, you know what to do instead.

## Usage

Learn Stylish by example. The following examples use [htm][5] instead of
[JSX][6], so they will run directly in a modern web browser, no transpilation
needed.

### Basic Example

```javascript
import { html } from 'htm/preact';
import { render } from 'preact';
import { stylish } from 'stylish-preact';

const RedText = stylish('div', `
color: red;
`);

render(html`
<${RedText}>Hi, I'm red!<//>
`, document.body);
```

### Example with props

```javascript
import { html } from 'htm/preact';
import { render } from 'preact';
import { stylish } from 'stylish-preact';

const Tint = stylish('div', (props) => `
color: ${props.color};
`);

render(html`
<${Tint} color="blue">Hi, I'm blue!<//>
`, document.body);
```

### Example with pseudo-classes

```javascript
import { html } from 'htm/preact';
import { render } from 'preact';
import { stylish } from 'stylish-preact';

const HoverGreen = stylish('div', {
rule: `
color: green;
`,
states: [':hover']
});

render(html`
<${HoverGreen}>Hi, hover me to make me green!<//>
`, document.body);
```

### Example with animation

```javascript
import { keyframes, stylish } from 'stylish-preact';

import { html } from 'htm/preact';
import { render } from 'preact';

const spin = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;

const Spinner = stylish('div', `
animation: 1s infinite linear ${spin};
`);

render(html`
<${Spinner}>Weeeeee!<//>
`, document.body);
```

### Example with inheritance

```javascript
import { html } from 'htm/preact';
import { render } from 'preact';
import { stylish } from 'stylish-preact';

const Big = stylish('span', `
font-size: 4em;
`);

const BigAndBlue = stylish(Big, `
color: blue;
`);

render(html`
<${BigAndBlue}>Hi, I'm big and blue.<//>
`, document.body);
```

### Example with theme

```javascript
import { Theme, stylish } from 'stylish-preact';

import { html } from 'htm/preact';
import { render } from 'preact';

const Logo = stylish('h1', ({ theme }) => `
color: ${theme.color.primary};
`);

render(html`
<${Theme.Provider} value=${{ color: { primary: 'purple' }}}>
<${Logo}>Purple Power<//>
<//>
`, document.body);
```

## Migrating from `@emotion/styled` or `styled-components`?

There are some notable differences between Stylish and these two libraries.

* Stylish does not support **nested** rules. It is primitive in that way.
Each set of properties must be defined in its own *rule*, complete with
any `media` selector and/or `states` (pseudo-classes or other criteria)
applicable to that rule.

* The `stylish` function behaves differently than the `styled` function
for either of these two libraries. Read through the examples and usage
documentation prior to reporting issues. Most notably, `stylish` should
not be used as a tagged template.

## API Reference

### `import { Theme } from 'stylish-preact';`

`Theme` is a [context][3]. To provide a theme, `<Theme.Provider value={theme}>...<//>`.
This theme will be automatically injected to all stylish components. To access
the theme directly, `const theme = useContext(Theme);`, just like you would any
other context.

The shape of the theme you define is completely up to you. Stylish does not use
it directly; it only passes it through, if present, so your stylish components
have access to it without having to do any additional work.

### `import { keyframes } from 'stylish-preact';`

The `keyframes` function can be used as a tagged template or invoked directly.
Use it to define an animation. It returns the animation's *name* which can then
be injected into other components to use that animation.

### `import { stylish } from 'stylish-preact';`

The `stylish` function creates a stylish component. It takes two arguments:

#### `stylish(Component, ...)`

The **first** argument *may* be the `string` name of the HTML element to
serve as the component's base element.

Alternatively, the **first** argument *may* be a reference to another
component to extend. For example, `const Convertible = stylish(Vehicle);`.

#### `stylish(..., outfit)`

The **second** argument is where the whole outfit comes together. It can be a
string, a function, an object, or an array of strings/functions/objects. These all define
aspects of how the stylish component is dressed.

##### String

When `outfit` is a string, it should contain a set of static CSS properties
describing the component's appearance.

##### Function

Whe `outfit` is a function, it should **expect** to be given the `props` given
to the component when it is rendered, and should **return** a string containing
the set of CSS properties describing the component's appearance.

##### Object

When `outfit` is an object, it should contain the following properties:

* `rule` - Required. This should be either a `string` or a `function` which
behaves as described above.

* `media` - Optional. If present, this should be a **media selector** `string`.
For example, `(max-width: 500px)`.

* `states` - Optional. If present, this should be `string[]`, where each item
is a suffix to be added to the component's CSS selector for this rule block.
For example, a simple `:hover` or a more complex `:not(:last-of-type)::after`.

##### Array

When `outfit` is an array, each item in the array should conform to the above
requirements depending on the type of that item (`string` vs `function` vs
`object`).

[1]: https://emotion.sh/docs/styled
[2]: https://styled-components.com/
[3]: https://preactjs.com/guide/v10/context/
[4]: https://preactjs.com/
[5]: https://github.com/developit/htm
[6]: https://reactjs.org/docs/introducing-jsx.html
99 changes: 99 additions & 0 deletions demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en_US">
<head>
<meta charset="UTF-8"/>
<script async src="/node_modules/es-module-shims/dist/es-module-shims.js"></script>
</head>
<body>
<script type="importmap">
{
"imports": {
"htm": "./node_modules/htm/dist/htm.mjs",
"htm/preact": "./node_modules/htm/preact/index.mjs",
"preact": "./node_modules/preact/dist/preact.mjs",
"preact/hooks": "./node_modules/preact/hooks/dist/hooks.mjs"
}
}
</script>
<script type="module">
import { Theme, keyframes, stylish } from './src/index.mjs';
import { html } from 'htm/preact';
import { render } from 'preact';

const energy = keyframes`
0% {
background-color: rgba(220, 255, 255, 0);
}
50% {
background-color: rgba(255, 255, 220, 100%);
}
100% {
background-color: rgba(220, 255, 255, 0);
}
`;

const spin = keyframes`
0% {
transform: rotate(0deg) scale(1, 1);
}
50% {
transform: rotate(180deg) scale(0.5, 0.5);
}
100% {
transform: rotate(360deg) scale(1, 1);
}
`;

const Spinning = stylish('span', ({ theme }) => `
align-items: center;
animation: infinite 1s linear ${spin};
border-color: transparent;
border-radius: 2px;
border-style: solid;
border-width: 2px;
box-sizing: border-box;
cursor: default;
display: flex;
font-size: ${theme.size / 2}px;
line-height: ${theme.size}px;
justify-content: center;
width: ${theme.size}px;
`);

const Hat = stylish(Spinning, {
rule: `
border-color: #0c0;
`,
states: [':hover']
});

const App = stylish('div', `
align-items: center;
animation: infinite 1.75s ease-in-out ${energy};
bottom: 0;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
left: 0;
overflow: hidden;
position: absolute;
right: 0;
top: 0;
width: 100%;
`);

render(html`
<${Theme.Provider} value=${{ size: 48 }}>
<${App}>
<${Hat} title="Stylish!">🎩<//>
<//>
<//>
`, document.body);
</script>
</body>
</html>
Loading

0 comments on commit d499702

Please sign in to comment.