diff --git a/README.md b/README.md
index a581da48..aa76c4a1 100644
--- a/README.md
+++ b/README.md
@@ -7,26 +7,21 @@ UI components for Hypothesis front-end applications.
Your project must have `preact` and `tailwindcss` as dependencies.
```sh
-$ yarn add preact tailwindcss
+$ yarn add preact tailwindcss @tailwindcss/postcss
$ yarn add @hypothesis/frontend-shared
```
-### tailwindcss configuration
+In your project's CSS entry point, add a `@source` for the frontend-shared
+package and import the Tailwind theme:
-Update your project's tailwind configuration:
+```css
+@import 'tailwindcss' source(none);
-```js
-import tailwindConfig from '@hypothesis/frontend-shared/lib/tailwind.preset.js';
-
-export default {
- // Use this package's preset
- presets: [tailwindConfig],
- content: [
- // Be sure to add this project's component source to your
- // tailwind content globs
- './node_modules/@hypothesis/frontend-shared/lib/**/*.{js,ts,tsx}',
- ],
- // ...
+/* Configure source files to scan for Tailwind classes. */
+@source './node_modules/@hypothesis/frontend-shared/lib/**/*.js';
+
+/* Import theme and utilities from shared package. */
+@import '@hypothesis/frontend-shared/tailwind-config.css';
```
## Documentation
diff --git a/package.json b/package.json
index 5bbb3758..7898ecb2 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,6 @@
"exports": {
".": "./lib/index.js",
"./pattern-library": "./lib/pattern-library/index.js",
- "./tailwind.preset.js": "./lib/tailwind.preset.js",
"./tailwind-config.css": "./src/tailwind-config.css"
},
"browserslist": "chrome 70, firefox 70, safari 11.1",
diff --git a/src/tailwind.focus-visible-ring.js b/src/tailwind.focus-visible-ring.js
deleted file mode 100644
index ab767ac8..00000000
--- a/src/tailwind.focus-visible-ring.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import plugin from 'tailwindcss/plugin.js';
-
-/**
- * Provide the `.focus-visible-ring` utility class.
- *
- * Add a utility class that will render a focus ring on a focused element
- * only when it has keyboard focus, or otherwise has `:focus-visible`. Do not
- * show a focus ring if the element has focus but is not `:focus-visible`.
- *
- * This plugin should only be necessary until such a time that we feel browser
- * support for :focus-visible is acceptable. At that time, what this plugin
- * does should be achievable with a few standard tailwind utility classes.
- *
- * This styling requires the browser to support the :focus-visible
- * pseudo-selector [1] or for the JS polyfill [2] to be loaded.
- *
- * [1] https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible
- * [2] https://github.com/WICG/focus-visible
- *
- * This can be combined with other Tailwind utility classes to customize.
- * Example Usage:
- * -
- * Show a focus-visible ring in the theme's `ringColor`
- * -
- * Same as above, but explicitly inset
- * -
- * Set ring shadow offset
- * -
- * Set ring color
- *
- * Ring width is this theme's `ringWidth.DEFAULT`
- */
-
-/** @type {any} */
-export const focusVisibleRing = plugin(({ addUtilities, theme }) => {
- const ringWidth = theme('ringWidth.DEFAULT');
- // Based on TW ring/shadow rules https://tailwindcss.com/docs/ring-width
- const focusRing = `var(--tw-ring-inset) 0 0 0 calc(${ringWidth} + var(--tw-ring-offset-width)) var(--tw-ring-color)`;
-
- addUtilities({
- '.focus-visible-ring': {
- // 1. set a visible focus ring if this element has focus (fallback)
- '&:focus': {
- boxShadow: focusRing,
- outline: 'none',
- },
- // 2. set a visible focus ring if this element has `:focus-visible`
- '&:focus-visible': {
- boxShadow: focusRing,
- outline: 'none',
- },
- // 3. Remove focus ring if element is focused but does not have
- // `:focus-visible` (in browsers that support `:focus-visible`)
- '&:focus:not(:focus-visible)': {
- boxShadow: 'none',
- },
- // 4. Remove focus ring if element is focused but does not have
- // a focus-visible class set by the polyfill
- '.js-focus-visible &:focus:not(.focus-visible)': {
- boxShadow: 'none',
- },
- },
- });
-});
diff --git a/src/tailwind.preset.js b/src/tailwind.preset.js
deleted file mode 100644
index 01787472..00000000
--- a/src/tailwind.preset.js
+++ /dev/null
@@ -1,172 +0,0 @@
-import colors from 'tailwindcss/colors.js';
-import plugin from 'tailwindcss/plugin.js';
-
-import { focusVisibleRing } from './tailwind.focus-visible-ring.js';
-import { scrollShadows } from './tailwind.scroll-shadows.js';
-
-// Equivalent to spacing value 11; minimum touch-target size
-const minimumTouchDimension = '44px';
-
-// Default colors
-const green = '#00a36d';
-const yellow = '#fbc168';
-const red = '#d93c3f';
-
-export default /** @type {Partial} */ ({
- theme: {
- extend: {
- animation: {
- 'fade-in': 'fade-in 0.3s forwards',
- 'fade-out': 'fade-out 0.3s forwards',
- },
- borderColor: {
- DEFAULT: '#dbdbdb',
- },
- boxShadow: {
- DEFAULT: '0 1px 1px rgba(0, 0, 0, 0.1)',
- md: '0px 2px 3px 0px rgba(0, 0, 0, 0.15)',
- },
- colors: {
- transparent: 'transparent',
- current: 'currentColor',
- white: colors.white,
- black: colors.black,
- grey: {
- 0: '#fafafa',
- 1: '#f2f2f2',
- 2: '#ececec',
- 3: '#dbdbdb',
- 4: '#a6a6a6',
- 5: '#9c9c9c',
- 6: '#737373',
- 7: '#595959',
- 8: '#3f3f3f',
- 9: '#202020',
- },
- slate: {
- 0: '#f4f4f6',
- 1: '#e3e3e5',
- 3: '#babac4',
- 5: '#9c9cab',
- 7: '#575768',
- 9: '#131316',
- },
- blue: {
- focus: '#59a7e8',
- },
- green: {
- light: '#dfebe7',
- DEFAULT: green,
- dark: '#005c3d',
- success: green,
- },
- yellow: {
- light: '#fef7ec',
- DEFAULT: yellow,
- dark: '#774903',
- notice: yellow,
- },
- red: {
- light: '#f0e2e3',
- DEFAULT: red,
- dark: '#891b1d',
- error: red,
- },
- brand: {
- dark: '#84141e',
- DEFAULT: '#bd1c2b',
- },
- // This naming makes color-related classnames generated from this
- // token less ambiguous. e.g. `bg-color-text` instead of `bg-text`
- 'color-text': {
- DEFAULT: '#202020',
- light: '#737373',
- inverted: '#f2f2f2',
- },
- },
- fontSize: {
- // Set font size to the maximum of 16px and the inherited size.
- //
- // On iOS, the input font size must be at least 16px to prevent the
- // browser from zooming into it on touch.
- 'at-least-16px': 'max(16px, 100%)',
- },
- keyframes: {
- 'fade-in': {
- '0%': {
- opacity: '0',
- },
- '100%': {
- opacity: '1',
- },
- },
- 'fade-out': {
- '0%': {
- opacity: '1',
- },
- '100%': {
- opacity: '0',
- },
- },
- },
- minHeight: {
- 'touch-minimum': minimumTouchDimension,
- },
- minWidth: {
- 'touch-minimum': minimumTouchDimension,
- },
- ringColor: {
- DEFAULT: '#59a7e8',
- },
- ringOpacity: {
- // Tailwind's default ring opacity is `0.5`
- DEFAULT: '1.0',
- },
- ringWidth: {
- DEFAULT: '2px',
- },
- screens: {
- tall: { raw: '(min-height: 32rem)' },
- touch: { raw: '(pointer: coarse)' },
- },
- spacing: {
- em: '1em',
- '2em': '2em',
- '4em': '4em',
- 'touch-minimum': minimumTouchDimension,
- },
- zIndex: {
- 1: '1',
- 2: '2',
- 3: '3',
- 4: '4',
- 5: '5',
- 10: '10',
- max: '2147483647',
- },
- },
- },
- plugins: [
- // Make `.focus-visible-ring` an available utility class
- focusVisibleRing,
- // Add `.scroll-shadows` for use by Scroll components
- scrollShadows,
- plugin(({ addVariant }) => {
- // Add a custom variant to mark an element to serve as a container for
- // a set of grouped input components. This is the same functionality
- // as Tailwind's built-in "group" variant, but with a different name for
- // clarity of purpose.
- // See https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
- addVariant('input-group', '.input-group &');
- }),
- plugin(({ addUtilities }) => {
- addUtilities({
- // Tailwind v3 does not provide this specific break utility: https://v3.tailwindcss.com/docs/word-break
- // Tailwind v4 does have an equivalent utility that we can eventually adopt https://tailwindcss.com/docs/overflow-wrap#wrapping-anywhere
- '.hyp-wrap-anywhere': {
- 'overflow-wrap': 'anywhere',
- },
- });
- }),
- ],
-});
diff --git a/src/tailwind.scroll-shadows.js b/src/tailwind.scroll-shadows.js
deleted file mode 100644
index 9bd79d1f..00000000
--- a/src/tailwind.scroll-shadows.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import plugin from 'tailwindcss/plugin.js';
-
-/**
- * Utility class to show scroll-hint shadows at the top and bottom of a
- * scrollable frame element if:
- * - The content height exceeds the frame height: i.e. can be scrolled, and
- * - The content is scrollable in the shadow's direction (up or down)
- *
- * Shadows are not visible once the frame has been scrolled all the way in the
- * shadow's direction. Shadows are not visible if the height of the content
- * does not overflow the frame (is not scrollable).
- *
- * The shadow hinting is handled by four positioned background gradients:
- * - One gradient each at top and bottom of frame that obscure the shadow hints
- * (shadow covers). These use `background-attachment: local`, which makes
- * their position fixed to the _content_ within the scrollbox.
- * - One gradient each at the top and the bottom of the frame that are the
- * shadow hints (shadows). These use `background-attachment: scroll` such
- * that they are always positioned at the top and the bottom of the
- * _scrollbox_ frame. When these positions align with the positions of the
- * shadow covers--at the top and the bottom of the overflowing content--
- * they will be obscured by those shadow covers.
- *
- * See https://lea.verou.me/2012/04/background-attachment-local/
- *
- * Safari's behavior can be different because of a bug with
- * `background-attachment: local`.
- * See https://bugs.webkit.org/show_bug.cgi?id=219324
- * In Safari < 15.4:
- * - Scroll-hint shadows do not appear if content does not overflow (this is
- * consistent with other browsers)
- * - Only the bottom scroll-hint shadow appears if content overflows
- * - The bottom scroll-hint shadow is always present, even if content is
- * fully scrolled
- */
-
-/** @type {any} */
-export const scrollShadows = plugin(({ addUtilities }) => {
- // These "shadow covers" scroll with the content. They align with and
- // obscure the shadows when an element is scrolled all the way in one
- // direction. If there is no overflow (nothing to scroll), these covers keep
- // any shadows from showing.
- const topShadowCover =
- 'linear-gradient(white 30%, rgba(255, 255, 255, 0)) 0 0';
- const bottomShadowCover =
- 'linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%';
-
- // The shadows are in a fixed position (`background-attachment: scroll`)
- // relative to the scrolling container.
- const topShadow =
- 'linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.05) 5px, rgba(255, 255, 255, 0) 70%) 0 0';
- const bottomShadow =
- 'linear-gradient(to top, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.05) 5px, rgba(255, 255, 255, 0) 70%) 0 100%';
-
- addUtilities({
- '.scroll-shadows': {
- background: `${topShadowCover}, ${bottomShadowCover}, ${topShadow}, ${bottomShadow}`,
- backgroundRepeat: 'no-repeat',
- backgroundColor: 'white',
- backgroundSize: '100% 40px, 100% 40px, 100% 14px, 100% 14px',
- backgroundAttachment: 'local, local, scroll, scroll',
- },
- });
-});
diff --git a/styles/test.css b/styles/test.css
index d5327003..12b5c2da 100644
--- a/styles/test.css
+++ b/styles/test.css
@@ -1,6 +1,4 @@
-/**
- * Stylesheet for tests
- */
+/* Stylesheet for tests. */
@import 'tailwindcss';
-@config '../tailwind.config.js';
+@import '../src/tailwind-config.css';
diff --git a/tailwind.config.js b/tailwind.config.js
deleted file mode 100644
index 94aaed2b..00000000
--- a/tailwind.config.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Tailwind configuration for local pattern library styles
- */
-import tailwindPreset from './src/tailwind.preset.js';
-
-export default {
- presets: [tailwindPreset],
- content: ['./src/**/*.{js,ts,tsx}', './templates/**/*.html'],
-
- // Pattern library specific config
- theme: {
- extend: {
- animation: {
- 'slide-in-from-right': 'slide-in-from-right 0.3s forwards ease-in-out',
- 'slide-out-to-right': 'slide-out-to-right 0.3s forwards ease-in-out',
- },
- keyframes: {
- 'slide-in-from-right': {
- '0%': {
- opacity: '0',
- left: '100%',
- },
- '80%': {
- left: '-10px',
- },
- '100%': {
- left: '0',
- opacity: '1',
- },
- },
- 'slide-out-to-right': {
- '0%': {
- left: '0',
- opacity: '1',
- },
- '20%': {
- left: '-10px',
- },
- '100%': {
- opacity: '0',
- left: '100%',
- },
- },
- },
- },
- },
-};