-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
5,056 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env*.local | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/** @type {import("@types/prettier").Options} */ | ||
module.exports = { | ||
printWidth: 100, | ||
useTabs: true, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Changelog | ||
|
||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
||
## [0.1.0] - 2023-09-09 | ||
|
||
Initial release! | ||
|
||
[unreleased]: https://github.com/ekafyi/tailwindcss-view-transitions/compare/v0.1.0...HEAD | ||
[0.1.0]: https://github.com/ekafyi/tailwindcss-view-transitions/releases/tag/v0.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# tailwindcss-view-transitions | ||
|
||
[![NPM package version](https://img.shields.io/npm/v/tailwindcss-view-transitions)](https://www.npmjs.com/package/tailwindcss-view-transitions) | ||
|
||
A plugin for customizing styles for the [View Transitions Web API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API). | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install -D tailwindcss-view-transitions | ||
``` | ||
|
||
Then add the plugin to your `tailwind.config.js` file: | ||
|
||
```js | ||
// tailwind.config.js | ||
module.exports = { | ||
theme: { | ||
// ... | ||
}, | ||
plugins: [ | ||
require("tailwindcss-view-transitions"), | ||
// ... | ||
], | ||
} | ||
``` | ||
|
||
## Usage | ||
|
||
Use the `vt-name-[ANY_STRING]` utility class to [create a separate view transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API#different_transitions_for_different_elements) on specific elements. | ||
|
||
```html | ||
<div class="vt-name-[main-header]"> | ||
</div> | ||
``` | ||
|
||
Use `vt-name-none` to disable a view transition. Can be used with any Tailwind variant, such as `md:*`. | ||
|
||
```html | ||
<div class="vt-name-[main-header] md:vt-name-none"> | ||
</div> | ||
|
||
<div class="vt-name-[main-header] motion-reduce:vt-name-none"> | ||
</div> | ||
``` | ||
|
||
The name can be any string except `root` (❌ `vt-name-[root]`), which is reserved for the default top-level view transition. | ||
|
||
| Class | CSS properties | | ||
| --- | --- | | ||
| `vt-name-[foo]` | `view-transition-name: foo;` | | ||
| `vt-name-[foo-bar]` | `view-transition-name: foo-bar;` | | ||
| `vt-name-none` | `view-transition-name: none;` | | ||
|
||
### Styling with CSS | ||
|
||
Style the view transition pseudo-elements from your global CSS file. | ||
|
||
```css | ||
/* input.css */ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; | ||
|
||
::view-transition-old(root), | ||
::view-transition-new(root) { | ||
animation: none; | ||
} | ||
|
||
::view-transition-old(main-content) { | ||
/* Add custom animation or style here */ | ||
/* animation: ... */ | ||
} | ||
|
||
::view-transition-new(main-content) { | ||
/* Add custom animation or style here */ | ||
/* animation: ... */ | ||
} | ||
``` | ||
|
||
### Configuration | ||
|
||
Alternatively, you can define styles from plugin configuration in your `tailwind.config.js` file. | ||
|
||
```js | ||
// tailwind.config.js | ||
module.exports = { | ||
plugins: [ | ||
require("tailwindcss-view-transitions")({ | ||
disableAllReduceMotion: false, | ||
styles: { | ||
// ... | ||
}, | ||
}), | ||
// ... other plugins | ||
], | ||
} | ||
``` | ||
|
||
## Options | ||
|
||
The plugin config accepts an options object as argument which contains these properties. All are optional. | ||
|
||
### `disableAllReduceMotion` | ||
|
||
- Type: `boolean` | ||
- Default: `false` | ||
|
||
Disables _all_ view transitions animation if user has set preference for reduced motion. (Note: Consider [this point](https://developer.chrome.com/docs/web-platform/view-transitions/#:~:text=a%20preference%20for%20%27reduced%20motion%27%20doesn%27t%20mean%20the%20user%20wants%20no%20motion) before disabling animations completely.) | ||
|
||
If `true`, it applies this code globally: | ||
|
||
```css | ||
@media (prefers-reduced-motion) { | ||
::view-transition-group(*),::view-transition-old(*),::view-transition-new(*) { | ||
animation: none !important; | ||
} | ||
} | ||
``` | ||
|
||
### `styles` | ||
|
||
- Type: `Record<string, CSSRuleObject & { old?: CSSRuleObject; new?: CSSRuleObject }>` | ||
- Default: `{}` | ||
|
||
Defines CSS styles for the view transition pseudo-elements. | ||
|
||
The styles object may contain any number of properties. | ||
|
||
- The **key** is the view transition name (`root` or any string value assigned [here](#usage)) | ||
- The **value** is one or more of these: | ||
- a [CSS rule object](https://github.com/tailwindlabs/tailwindcss/blob/9faf10958b880067cacdd0ef3c4bf9e64172ed91/types/config.d.ts#L15), which will be applied to both outgoing (`::view-transition-old(VT_NAME)`) and incoming (`::view-transition-new(VT_NAME)`) pseudo-elements | ||
- a propery `old` containing a CSS rule object, which will be applied only to `::view-transition-old(VT_NAME)` | ||
- a propery `new` containing a CSS rule object, which will be applied only to `::view-transition-new(VT_NAME)` | ||
|
||
| styles config | Generated CSS | | ||
| --- | --- | | ||
| <pre>{ <br/> root: { animation: "none" },<br/>}</pre> | <pre>::view-transition-old(root),<br/>::view-transition-new(root) {<br/> animation: none;<br/>}</pre> | | ||
| <pre>{ <br/> root: { <br/> old: { animationDuration: "1s" },<br/> new: { animationDuration: "3s" },<br/> },<br/>}</pre> | <pre>::view-transition-old(root) {<br/> animation-duration: 1s;<br/>}<br/>::view-transition-new(root) {<br/> animation-duration: 3s;<br/>}</pre> | | ||
| <pre>{ <br/> root: { animation: "none" },<br/> "main-content": { <br/> old: { animationDuration: "1s" },<br/> new: { animationDuration: "3s" },<br/> },<br/>}</pre> | <pre>::view-transition-old(root),<br/>::view-transition-new(root) {<br/> animation: none;<br/>}<br/><br/>::view-transition-old(main-content) {<br/> animation-duration: 1s;<br/>}<br/>::view-transition-new(main-content) {<br/> animation-duration: 3s;<br/>}</pre> | | ||
|
||
⚠️ If applying custom CSS animation, you need to define `@keyframes` separately in your CSS file or through [Tailwind theme configuration](https://tailwindcss.com/docs/animation#customizing-your-theme), or alternatively use an existing `@keyframes` animation. | ||
|
||
Detailed examples: https://github.com/ekafyi/tailwindcss-view-transitions/blob/main/docs/examples.md | ||
|
||
## When not to use? | ||
|
||
You may not need this plugin if: | ||
|
||
* You don’t need to customize the browser default transition (crossfade) styles 😁 | ||
* You do styling outside of Tailwind configuration | ||
* You exclusively use a (meta)framework that has its own API for conveniently styling view transitions, such as [Astro](https://docs.astro.build/en/guides/view-transitions/) | ||
|
||
As an unofficial plugin, it will be deprecated when/if Tailwind adds an official plugin for styling view transitions. | ||
|
||
## Bugs & feature requests | ||
|
||
While I'm not actively accepting feature requests, I outlined future plans in the [Discussions](https://github.com/ekafyi/tailwindcss-view-transitions/discussions). | ||
|
||
Found a bug? Feel free to [open an issue](https://github.com/ekafyi/tailwindcss-view-transitions/issues). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Examples | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// From https://github.com/tailwindlabs/tailwindcss-line-clamp/blob/master/jest/customMatchers.js | ||
const prettier = require('prettier') | ||
const { diff } = require('jest-diff') | ||
|
||
function format(input) { | ||
return prettier.format(input, { | ||
parser: 'css', | ||
printWidth: 100, | ||
}) | ||
} | ||
|
||
expect.extend({ | ||
// Compare two CSS strings with all whitespace removed | ||
// This is probably naive but it's fast and works well enough. | ||
toMatchCss(received, argument) { | ||
function stripped(str) { | ||
return str.replace(/\s/g, '').replace(/;/g, '') | ||
} | ||
|
||
const options = { | ||
comment: 'stripped(received) === stripped(argument)', | ||
isNot: this.isNot, | ||
promise: this.promise, | ||
} | ||
|
||
const pass = stripped(received) === stripped(argument) | ||
|
||
const message = pass | ||
? () => { | ||
return ( | ||
this.utils.matcherHint('toMatchCss', undefined, undefined, options) + | ||
'\n\n' + | ||
`Expected: not ${this.utils.printExpected(format(received))}\n` + | ||
`Received: ${this.utils.printReceived(format(argument))}` | ||
) | ||
} | ||
: () => { | ||
const actual = format(received) | ||
const expected = format(argument) | ||
|
||
const diffString = diff(expected, actual, { | ||
expand: this.expand, | ||
}) | ||
|
||
return ( | ||
this.utils.matcherHint('toMatchCss', undefined, undefined, options) + | ||
'\n\n' + | ||
(diffString && diffString.includes('- Expect') | ||
? `Difference:\n\n${diffString}` | ||
: `Expected: ${this.utils.printExpected(expected)}\n` + | ||
`Received: ${this.utils.printReceived(actual)}`) | ||
) | ||
} | ||
|
||
return { actual: received, message, pass } | ||
}, | ||
toIncludeCss(received, argument) { | ||
const options = { | ||
comment: 'stripped(received).includes(stripped(argument))', | ||
isNot: this.isNot, | ||
promise: this.promise, | ||
} | ||
|
||
const actual = format(received) | ||
const expected = format(argument) | ||
|
||
const pass = actual.includes(expected) | ||
|
||
const message = pass | ||
? () => { | ||
return ( | ||
this.utils.matcherHint('toIncludeCss', undefined, undefined, options) + | ||
'\n\n' + | ||
`Expected: not ${this.utils.printExpected(format(received))}\n` + | ||
`Received: ${this.utils.printReceived(format(argument))}` | ||
) | ||
} | ||
: () => { | ||
const diffString = diff(expected, actual, { | ||
expand: this.expand, | ||
}) | ||
|
||
return ( | ||
this.utils.matcherHint('toIncludeCss', undefined, undefined, options) + | ||
'\n\n' + | ||
(diffString && diffString.includes('- Expect') | ||
? `Difference:\n\n${diffString}` | ||
: `Expected: ${this.utils.printExpected(expected)}\n` + | ||
`Received: ${this.utils.printReceived(actual)}`) | ||
) | ||
} | ||
|
||
return { actual: received, message, pass } | ||
}, | ||
}) | ||
|
||
expect.extend({ | ||
// Compare two CSS strings with all whitespace removed | ||
// This is probably naive but it's fast and works well enough. | ||
toMatchFormattedCss(received, argument) { | ||
function format(input) { | ||
return prettier.format(input.replace(/\n/g, ''), { | ||
parser: 'css', | ||
printWidth: 100, | ||
}) | ||
} | ||
const options = { | ||
comment: 'stripped(received) === stripped(argument)', | ||
isNot: this.isNot, | ||
promise: this.promise, | ||
} | ||
|
||
let formattedReceived = format(received) | ||
let formattedArgument = format(argument) | ||
|
||
const pass = formattedReceived === formattedArgument | ||
|
||
const message = pass | ||
? () => { | ||
return ( | ||
this.utils.matcherHint('toMatchCss', undefined, undefined, options) + | ||
'\n\n' + | ||
`Expected: not ${this.utils.printExpected(formattedReceived)}\n` + | ||
`Received: ${this.utils.printReceived(formattedArgument)}` | ||
) | ||
} | ||
: () => { | ||
const actual = formattedReceived | ||
const expected = formattedArgument | ||
|
||
const diffString = diff(expected, actual, { | ||
expand: this.expand, | ||
}) | ||
|
||
return ( | ||
this.utils.matcherHint('toMatchCss', undefined, undefined, options) + | ||
'\n\n' + | ||
(diffString && diffString.includes('- Expect') | ||
? `Difference:\n\n${diffString}` | ||
: `Expected: ${this.utils.printExpected(expected)}\n` + | ||
`Received: ${this.utils.printReceived(actual)}`) | ||
) | ||
} | ||
|
||
return { actual: received, message, pass } | ||
}, | ||
}) |
Oops, something went wrong.