├── .editorconfig
├── .gitignore
├── .rollup.js
├── .tape.js
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── INSTALL-POSTCSS.md
├── LICENSE.md
├── README-BROWSER.md
├── README-POSTCSS.md
├── README.md
├── package.json
├── src
├── browser.js
├── cli.js
└── postcss.js
└── test
├── basic.css
├── basic.expect.css
├── basic.preserve.expect.css
└── basic.replacewith.expect.css
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_style = tab
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
13 | [*.{json,md,yml}]
14 | indent_size = 2
15 | indent_style = space
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /browser.js*
3 | /cli.js*
4 | /index.js*
5 | /index.mjs*
6 | /postcss.js*
7 | /postcss.mjs*
8 | package-lock.json
9 | *.log*
10 | *.result.css
11 | .*
12 | !.appveyor.yml
13 | !.editorconfig
14 | !.gitignore
15 | !.rollup.js
16 | !.tape.js
17 | !.travis.yml
18 |
--------------------------------------------------------------------------------
/.rollup.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import { terser } from 'rollup-plugin-terser';
3 |
4 | const isBrowser = String(process.env.NODE_ENV).includes('browser');
5 | const isCLI = String(process.env.NODE_ENV).includes('cli');
6 | const isPostCSS = String(process.env.NODE_ENV).includes('postcss');
7 | const targets = isCLI || isPostCSS || !isBrowser ? { node: 6 } : 'last 2 versions, not dead';
8 |
9 | const input = `src/${isCLI ? 'cli' : isPostCSS ? 'postcss' : 'browser'}.js`;
10 | const output = isCLI
11 | ? { file: 'cli.js', format: 'cjs' }
12 | : isBrowser
13 | ? { file: 'browser.js', format: 'cjs' }
14 | : isPostCSS
15 | ? [
16 | { file: 'postcss.js', format: 'cjs', sourcemap: true },
17 | { file: 'postcss.mjs', format: 'esm', sourcemap: true }
18 | ] : [
19 | { file: 'index.js', format: 'cjs', sourcemap: true },
20 | { file: 'index.mjs', format: 'esm', sourcemap: true }
21 | ];
22 | const plugins = [
23 | babel({
24 | presets: [
25 | ['@babel/env', { targets }]
26 | ]
27 | })
28 | ].concat(isBrowser
29 | ? [
30 | trimContentForBrowser(),
31 | terser()
32 | ]
33 | : isCLI
34 | ? [
35 | trimContentForBrowser(),
36 | addHashBang()
37 | ]
38 | : []);
39 |
40 | export default { input, output, plugins };
41 |
42 | function addHashBang() {
43 | return {
44 | name: 'add-hash-bang',
45 | renderChunk(code) {
46 | const updatedCode = `#!/usr/bin/env node\n\n${code}`;
47 |
48 | return updatedCode;
49 | }
50 | };
51 | }
52 |
53 | function trimContentForBrowser() {
54 | return {
55 | name: 'trim-content-for-browser',
56 | renderChunk(code) {
57 | const updatedCode = code
58 | .replace(/'use strict';\n*/, '')
59 | .replace(/\n*module\.exports = focusWithin;/, '');
60 |
61 | return updatedCode;
62 | }
63 | };
64 | }
65 |
--------------------------------------------------------------------------------
/.tape.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'focus-within': {
3 | 'basic': {
4 | message: 'supports basic usage'
5 | },
6 | 'basic:replacewith': {
7 | message: 'supports { replaceWith: ".focus-within" } usage',
8 | options: {
9 | replaceWith: '.focus-within'
10 | }
11 | },
12 | 'basic:preserve': {
13 | message: 'supports { preserve: false } usage',
14 | options: {
15 | preserve: false
16 | }
17 | }
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # https://docs.travis-ci.com/user/travis-lint
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - 6
7 |
8 | install:
9 | - npm install --ignore-scripts
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changes to Focus Within
2 |
3 | ### 3.0.2 (November 23, 2018)
4 |
5 | - Improve CLI usage
6 | - Futher improve documentation
7 |
8 | ### 3.0.1 (November 21, 2018)
9 |
10 | - Fix an issue with the main module not being published
11 | - Update `postcss` to 7.0.6 (patch)
12 |
13 | ### 3.0.0 (November 14, 2018)
14 |
15 | - Rewrite the source for optimal browser size
16 | - Publish a browser-ready version
17 |
18 | ### 2.0.0 (June 4, 2018)
19 |
20 | - Default polyfill to only execute in unsupported browsers
21 | - Add option to force polyfill
22 |
23 | ### 1.0.1 (February 20, 2018)
24 |
25 | - Updated build compatibility
26 |
27 | ### 1.0.0 (February 17, 2018)
28 |
29 | - Initial version
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Focus Within
2 |
3 | You want to help? You rock! Now, take a moment to be sure your contributions
4 | make sense to everyone else.
5 |
6 | ## Reporting Issues
7 |
8 | Found a problem? Want a new feature?
9 |
10 | - See if your issue or idea has [already been reported].
11 | - Provide a [reduced test case] or a [live example].
12 |
13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code.
14 |
15 | ## Submitting Pull Requests
16 |
17 | Pull requests are the greatest contributions, so be sure they are focused in
18 | scope and avoid unrelated commits.
19 |
20 | 1. To begin; [fork this project], clone your fork, and add our upstream.
21 | ```bash
22 | # Clone your fork of the repo into the current directory
23 | git clone git@github.com:YOUR_USER/focus-within.git
24 |
25 | # Navigate to the newly cloned directory
26 | cd focus-within
27 |
28 | # Assign the original repo to a remote called "upstream"
29 | git remote add upstream git@github.com:jonathantneal/focus-within.git
30 |
31 | # Install the tools necessary for testing
32 | npm install
33 | ```
34 |
35 | 2. Create a branch for your feature or fix:
36 | ```bash
37 | # Move into a new branch for your feature
38 | git checkout -b feature/thing
39 | ```
40 | ```bash
41 | # Move into a new branch for your fix
42 | git checkout -b fix/something
43 | ```
44 |
45 | 3. If your code follows our practices, then push your feature branch:
46 | ```bash
47 | # Test current code
48 | npm test
49 | ```
50 | ```bash
51 | # Push the branch for your new feature
52 | git push origin feature/thing
53 | ```
54 | ```bash
55 | # Or, push the branch for your update
56 | git push origin update/something
57 | ```
58 |
59 | That’s it! Now [open a pull request] with a clear title and description.
60 |
61 | [already been reported]: issues
62 | [fork this project]: fork
63 | [live example]: https://codepen.io/pen
64 | [open a pull request]: https://help.github.com/articles/using-pull-requests/
65 | [reduced test case]: https://css-tricks.com/reduced-test-cases/
66 |
--------------------------------------------------------------------------------
/INSTALL-POSTCSS.md:
--------------------------------------------------------------------------------
1 | # Installing PostCSS
2 |
3 | [Focus Within] runs in all Node environments, with special instructions for:
4 |
5 | | [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) |
6 | | --- | --- | --- | --- | --- | --- |
7 |
8 | ## Node
9 |
10 | Add [Focus Within] to your project:
11 |
12 | ```bash
13 | npm install focus-within --save-dev
14 | ```
15 |
16 | Use [Focus Within] to process your CSS:
17 |
18 | ```js
19 | const postcssFocusWithin = require('focus-within/postcss');
20 |
21 | postcssFocusWithin.process(YOUR_CSS /*, processOptions, pluginOptions */);
22 | ```
23 |
24 | Or use it as a [PostCSS] plugin:
25 |
26 | ```js
27 | const postcss = require('postcss');
28 | const postcssFocusWithin = require('focus-within/postcss');
29 |
30 | postcss([
31 | postcssFocusWithin(/* pluginOptions */)
32 | ]).process(YOUR_CSS /*, processOptions */);
33 | ```
34 |
35 | ## PostCSS CLI
36 |
37 | Add [PostCSS CLI] to your project:
38 |
39 | ```bash
40 | npm install postcss-cli --save-dev
41 | ```
42 |
43 | Use [Focus Within] in your `postcss.config.js` configuration file:
44 |
45 | ```js
46 | const postcssFocusWithin = require('focus-within/postcss');
47 |
48 | module.exports = {
49 | plugins: [
50 | postcssFocusWithin(/* pluginOptions */)
51 | ]
52 | }
53 | ```
54 |
55 | ## Webpack
56 |
57 | Add [PostCSS Loader] to your project:
58 |
59 | ```bash
60 | npm install postcss-loader --save-dev
61 | ```
62 |
63 | Use [Focus Within] in your Webpack configuration:
64 |
65 | ```js
66 | const postcssFocusWithin = require('focus-within/postcss');
67 |
68 | module.exports = {
69 | module: {
70 | rules: [
71 | {
72 | test: /\.css$/,
73 | use: [
74 | 'style-loader',
75 | { loader: 'css-loader', options: { importLoaders: 1 } },
76 | { loader: 'postcss-loader', options: {
77 | ident: 'postcss',
78 | plugins: () => [
79 | postcssFocusWithin(/* pluginOptions */)
80 | ]
81 | } }
82 | ]
83 | }
84 | ]
85 | }
86 | }
87 | ```
88 |
89 | ## Create React App
90 |
91 | Add [React App Rewired] and [React App Rewire PostCSS] to your project:
92 |
93 | ```bash
94 | npm install react-app-rewired react-app-rewire-postcss --save-dev
95 | ```
96 |
97 | Use [React App Rewire PostCSS] and [Focus Within] in your `config-overrides.js`
98 | file:
99 |
100 | ```js
101 | const reactAppRewirePostcss = require('react-app-rewire-postcss');
102 | const postcssFocusWithin = require('focus-within/postcss');
103 |
104 | module.exports = config => reactAppRewirePostcss(config, {
105 | plugins: () => [
106 | postcssFocusWithin(/* pluginOptions */)
107 | ]
108 | });
109 | ```
110 |
111 | ## Gulp
112 |
113 | Add [Gulp PostCSS] to your project:
114 |
115 | ```bash
116 | npm install gulp-postcss --save-dev
117 | ```
118 |
119 | Use [Focus Within] in your Gulpfile:
120 |
121 | ```js
122 | const postcss = require('gulp-postcss');
123 | const postcssFocusWithin = require('focus-within/postcss');
124 |
125 | gulp.task('css', () => gulp.src('./src/*.css').pipe(
126 | postcss([
127 | postcssFocusWithin(/* pluginOptions */)
128 | ])
129 | ).pipe(
130 | gulp.dest('.')
131 | ));
132 | ```
133 |
134 | ## Grunt
135 |
136 | Add [Grunt PostCSS] to your project:
137 |
138 | ```bash
139 | npm install grunt-postcss --save-dev
140 | ```
141 |
142 | Use [Focus Within] in your Gruntfile:
143 |
144 | ```js
145 | const postcssFocusWithin = require('focus-within/postcss');
146 |
147 | grunt.loadNpmTasks('grunt-postcss');
148 |
149 | grunt.initConfig({
150 | postcss: {
151 | options: {
152 | use: [
153 | postcssFocusWithin(/* pluginOptions */)
154 | ]
155 | },
156 | dist: {
157 | src: '*.css'
158 | }
159 | }
160 | });
161 | ```
162 |
163 | [Focus Within]: https://github.com/jonathantneal/focus-within
164 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss
165 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss
166 | [PostCSS]: https://github.com/postcss/postcss
167 | [PostCSS CLI]: https://github.com/postcss/postcss-cli
168 | [PostCSS Loader]: https://github.com/postcss/postcss-loader
169 | [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss
170 | [React App Rewired]: https://github.com/timarney/react-app-rewired
171 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # CC0 1.0 Universal
2 |
3 | ## Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an “owner”) of an original work of
8 | authorship and/or a database (each, a “Work”).
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific works
12 | (“Commons”) that the public can reliably and without fear of later claims of
13 | infringement build upon, modify, incorporate in other works, reuse and
14 | redistribute as freely as possible in any form whatsoever and for any purposes,
15 | including without limitation commercial purposes. These owners may contribute
16 | to the Commons to promote the ideal of a free culture and the further
17 | production of creative, cultural and scientific works, or to gain reputation or
18 | greater distribution for their Work in part through the use and efforts of
19 | others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation of
22 | additional consideration or compensation, the person associating CC0 with a
23 | Work (the “Affirmer”), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and
25 | publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights (“Copyright and
31 | Related Rights”). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 | 1. the right to reproduce, adapt, distribute, perform, display, communicate,
34 | and translate a Work;
35 | 2. moral rights retained by the original author(s) and/or performer(s);
36 | 3. publicity and privacy rights pertaining to a person’s image or likeness
37 | depicted in a Work;
38 | 4. rights protecting against unfair competition in regards to a Work,
39 | subject to the limitations in paragraph 4(i), below;
40 | 5. rights protecting the extraction, dissemination, use and reuse of data in
41 | a Work;
42 | 6. database rights (such as those arising under Directive 96/9/EC of the
43 | European Parliament and of the Council of 11 March 1996 on the legal
44 | protection of databases, and under any national implementation thereof,
45 | including any amended or successor version of such directive); and
46 | 7. other similar, equivalent or corresponding rights throughout the world
47 | based on applicable law or treaty, and any national implementations
48 | thereof.
49 |
50 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
51 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
52 | unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright
53 | and Related Rights and associated claims and causes of action, whether now
54 | known or unknown (including existing as well as future claims and causes of
55 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
56 | duration provided by applicable law or treaty (including future time
57 | extensions), (iii) in any current or future medium and for any number of
58 | copies, and (iv) for any purpose whatsoever, including without limitation
59 | commercial, advertising or promotional purposes (the “Waiver”). Affirmer
60 | makes the Waiver for the benefit of each member of the public at large and
61 | to the detriment of Affirmer’s heirs and successors, fully intending that
62 | such Waiver shall not be subject to revocation, rescission, cancellation,
63 | termination, or any other legal or equitable action to disrupt the quiet
64 | enjoyment of the Work by the public as contemplated by Affirmer’s express
65 | Statement of Purpose.
66 |
67 | 3. Public License Fallback. Should any part of the Waiver for any reason be
68 | judged legally invalid or ineffective under applicable law, then the Waiver
69 | shall be preserved to the maximum extent permitted taking into account
70 | Affirmer’s express Statement of Purpose. In addition, to the extent the
71 | Waiver is so judged Affirmer hereby grants to each affected person a
72 | royalty-free, non transferable, non sublicensable, non exclusive,
73 | irrevocable and unconditional license to exercise Affirmer’s Copyright and
74 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
75 | maximum duration provided by applicable law or treaty (including future time
76 | extensions), (iii) in any current or future medium and for any number of
77 | copies, and (iv) for any purpose whatsoever, including without limitation
78 | commercial, advertising or promotional purposes (the “License”). The License
79 | shall be deemed effective as of the date CC0 was applied by Affirmer to the
80 | Work. Should any part of the License for any reason be judged legally
81 | invalid or ineffective under applicable law, such partial invalidity or
82 | ineffectiveness shall not invalidate the remainder of the License, and in
83 | such case Affirmer hereby affirms that he or she will not (i) exercise any
84 | of his or her remaining Copyright and Related Rights in the Work or (ii)
85 | assert any associated claims and causes of action with respect to the Work,
86 | in either case contrary to Affirmer’s express Statement of Purpose.
87 |
88 | 4. Limitations and Disclaimers.
89 | 1. No trademark or patent rights held by Affirmer are waived, abandoned,
90 | surrendered, licensed or otherwise affected by this document.
91 | 2. Affirmer offers the Work as-is and makes no representations or warranties
92 | of any kind concerning the Work, express, implied, statutory or
93 | otherwise, including without limitation warranties of title,
94 | merchantability, fitness for a particular purpose, non infringement, or
95 | the absence of latent or other defects, accuracy, or the present or
96 | absence of errors, whether or not discoverable, all to the greatest
97 | extent permissible under applicable law.
98 | 3. Affirmer disclaims responsibility for clearing rights of other persons
99 | that may apply to the Work or any use thereof, including without
100 | limitation any person’s Copyright and Related Rights in the Work.
101 | Further, Affirmer disclaims responsibility for obtaining any necessary
102 | consents, permissions or other rights required for any use of the Work.
103 | 4. Affirmer understands and acknowledges that Creative Commons is not a
104 | party to this document and has no duty or obligation with respect to this
105 | CC0 or use of the Work.
106 |
107 | For more information, please see
108 | http://creativecommons.org/publicdomain/zero/1.0/.
109 |
--------------------------------------------------------------------------------
/README-BROWSER.md:
--------------------------------------------------------------------------------
1 | # Focus Within for Browsers [][Focus Within]
2 |
3 | [![NPM Version][npm-img]][npm-url]
4 | [![Build Status][cli-img]][cli-url]
5 | [![Support Chat][git-img]][git-url]
6 |
7 | [Focus Within] lets you style elements based on whether they are focused or
8 | contain a focused element, following the [Selectors Level 4] specification.
9 |
10 | ```css
11 | .field label {
12 | /* style a label */
13 | }
14 |
15 | .field[focus-within] label {
16 | /* style a label when its field also contains a focused element */
17 | }
18 | ```
19 |
20 | ## Usage
21 |
22 | Add [Focus Within] to your build tool:
23 |
24 | ```bash
25 | npm install focus-within
26 | ```
27 |
28 | Then include and initialize it on your document:
29 |
30 | ```js
31 | const focusWithin = require('focus-within');
32 |
33 | focusWithin(document);
34 | ```
35 |
36 | ## Options
37 |
38 | [Focus Within] accepts a secondary paramater to configure the attribute or
39 | class name added to elements matching focused elements or containing focused
40 | elements.
41 |
42 | ```js
43 | focusWithin(document, {
44 | attr: false,
45 | className: '.focus-within'
46 | });
47 | ```
48 |
49 | Falsey values on either `attr` or `className` will disable setting the
50 | attribute or class name on elements matching `:focus-within`.
51 |
52 | [Focus Within] also accepts a secondary paramater to configure whether the
53 | polyfill is loaded regardless of support. If `force` is given a truthy value,
54 | then the polyfill will always execute.
55 |
56 | ```js
57 | focusWithin(document, {
58 | force: true
59 | });
60 | ```
61 |
62 | [cli-img]: https://img.shields.io/travis/jonathantneal/focus-within/master.svg
63 | [cli-url]: https://travis-ci.org/jonathantneal/focus-within
64 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg
65 | [git-url]: https://gitter.im/postcss/postcss
66 | [npm-img]: https://img.shields.io/npm/v/focus-within.svg
67 | [npm-url]: https://www.npmjs.com/package/focus-within
68 |
69 | [Focus Within]: https://github.com/jonathantneal/focus-within
70 | [Selectors Level 4]: https://www.w3.org/TR/selectors-4/#the-focus-within-pseudo
71 |
--------------------------------------------------------------------------------
/README-POSTCSS.md:
--------------------------------------------------------------------------------
1 | # Focus Within for PostCSS [
][Focus Within]
2 |
3 | [![NPM Version][npm-img]][npm-url]
4 | [![Build Status][cli-img]][cli-url]
5 | [![Support Chat][git-img]][git-url]
6 |
7 | [Focus Within] lets you style elements based on whether they are focused or
8 | contain a focused element, following the [Selectors Level 4] specification.
9 |
10 | ```css
11 | .field:focus-within label {
12 | font-weight: bold;
13 | }
14 |
15 | /* becomes */
16 |
17 | .field[focus-within] label {
18 | font-weight: bold;
19 | }
20 |
21 | .field:focus-within label {
22 | font-weight: bold;
23 | }
24 | ```
25 |
26 | [Focus Within] duplicates rules using the `:focus-within` pseudo-class
27 | with a `[focus-within]` attribute selector. This replacement selector can be
28 | changed using the `replaceWith` option. Also, the preservation of the original
29 | `:focus-within` rule can be disabled using the `preserve` option.
30 |
31 | ## Usage
32 |
33 | Add [Focus Within] to your project:
34 |
35 | ```bash
36 | npm install focus-within --save-dev
37 | ```
38 |
39 | Use [Focus Within] to process your CSS:
40 |
41 | ```js
42 | const postcssFocusWithin = require('focus-within/postcss');
43 |
44 | postcssFocusWithin.process(YOUR_CSS /*, processOptions, pluginOptions */);
45 | ```
46 |
47 | Or use it as a [PostCSS] plugin:
48 |
49 | ```js
50 | const postcss = require('postcss');
51 | const postcssFocusWithin = require('focus-within/postcss');
52 |
53 | postcss([
54 | postcssFocusWithin(/* pluginOptions */)
55 | ]).process(YOUR_CSS /*, processOptions */);
56 | ```
57 |
58 | [Focus Within] runs in all Node environments, with special
59 | instructions for:
60 |
61 | | [Node](INSTALL-POSTCSS.md#node) | [PostCSS CLI](INSTALL-POSTCSS.md#postcss-cli) | [Webpack](INSTALL-POSTCSS.md#webpack) | [Create React App](INSTALL-POSTCSS.md#create-react-app) | [Gulp](INSTALL-POSTCSS.md#gulp) | [Grunt](INSTALL-POSTCSS.md#grunt) |
62 | | --- | --- | --- | --- | --- | --- |
63 |
64 | ## Options
65 |
66 | ### preserve
67 |
68 | The `preserve` option defines whether the original selector should remain. By
69 | default, the original selector is preserved.
70 |
71 | ```js
72 | focusWithin({ preserve: false });
73 | ```
74 |
75 | ```css
76 | .my-form-field:focus-within label {
77 | background-color: yellow;
78 | }
79 |
80 | /* becomes */
81 |
82 | .my-form-field[focus-within] label {
83 | background-color: yellow;
84 | }
85 | ```
86 |
87 | ### replaceWith
88 |
89 | The `replaceWith` option defines the selector to replace `:focus-within`. By
90 | default, the replacement selector is `[focus-within]`.
91 |
92 | ```js
93 | focusWithin({ replaceWith: '.focus-within' });
94 | ```
95 |
96 | ```css
97 | .my-form-field:focus-within label {
98 | background-color: yellow;
99 | }
100 |
101 | /* becomes */
102 |
103 | .my-form-field.focus-within label {
104 | background-color: yellow;
105 | }
106 |
107 | .my-form-field:focus-within label {
108 | background-color: yellow;
109 | }
110 | ```
111 |
112 | [cli-img]: https://img.shields.io/travis/jonathantneal/focus-within/master.svg
113 | [cli-url]: https://travis-ci.org/jonathantneal/focus-within
114 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg
115 | [git-url]: https://gitter.im/postcss/postcss
116 | [npm-img]: https://img.shields.io/npm/v/focus-within.svg
117 | [npm-url]: https://www.npmjs.com/package/focus-within
118 |
119 | [Focus Within]: https://github.com/jonathantneal/focus-within
120 | [Selectors Level 4]: https://www.w3.org/TR/selectors-4/#the-focus-within-pseudo
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Focus Within [
][Focus Within]
2 |
3 | [![NPM Version][npm-img]][npm-url]
4 | [![Build Status][cli-img]][cli-url]
5 | [![Support Chat][git-img]][git-url]
6 |
7 | [Focus Within] lets you style elements when they are focused or contain a
8 | focused element, following the [Selectors Level 4] specification.
9 |
10 | ```css
11 | .field label {
12 | /* style a label */
13 | }
14 |
15 | .field:focus-within label {
16 | /* style a label when its field also contains a focused element */
17 | }
18 | ```
19 |
20 | ## Usage
21 |
22 | From the command line, transform CSS files that use `:focus-within` selectors:
23 |
24 | ```bash
25 | npx focus-within SOURCE.css TRANSFORMED.css
26 | ```
27 |
28 | Next, use your transformed CSS with this script:
29 |
30 | ```html
31 |
32 |
33 |
34 | ```
35 |
36 | That’s it. The script is 379 bytes and works in all browsers, including
37 | Internet Explorer 9.
38 |
39 | ## How it works
40 |
41 | The [PostCSS plugin](README-POSTCSS.md) duplicates rules containing
42 | `:focus-within`, replacing them with an alternative `[focus-within]` selector.
43 |
44 | ```css
45 | .field:focus-within label {
46 | font-weight: bold;
47 | }
48 |
49 | /* becomes */
50 |
51 | .field[focus-within] label {
52 | font-weight: bold;
53 | }
54 |
55 | .field:focus-within label {
56 | font-weight: bold;
57 | }
58 | ```
59 |
60 | Next, the [JavaScript library](README-BROWSER.md) adds a `focus-within`
61 | attribute to elements otherwise matching `:focus-within` natively.
62 |
63 | ```html
64 |
65 |
Some sibling text element.
77 | 78 | 79 | ``` 80 | 81 | > **GOTCHA!** 82 | > 83 | > One cannot simply add the `[focus-within]` selector to an existing rule: 84 | > ```css 85 | > .field:focus-within label, .field[focus-within] label {} 86 | > ``` 87 | > Browsers that don't support `:focus-within` will throw the entire rule away! 88 | > This is why you should follow the [Usage](#usage) instructions. 89 | 90 | [cli-img]: https://img.shields.io/travis/jonathantneal/focus-within/master.svg 91 | [cli-url]: https://travis-ci.org/jonathantneal/focus-within 92 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg 93 | [git-url]: https://gitter.im/postcss/postcss 94 | [npm-img]: https://img.shields.io/npm/v/focus-within.svg 95 | [npm-url]: https://www.npmjs.com/package/focus-within 96 | 97 | [Focus Within]: https://github.com/jonathantneal/focus-within 98 | [PostCSS Preset Env]: https://preset-env.cssdb.org/ 99 | [Selectors Level 4]: https://www.w3.org/TR/selectors-4/#the-focus-within-pseudo 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "focus-within", 3 | "version": "3.0.2", 4 | "description": "Style elements when they are focused or contain a focused element", 5 | "author": "Jonathan Neal