├── .eslintignore
├── .gitignore
├── .prettierignore
├── tests
├── red.png
├── green.png
├── simple_link.css
├── empty_href.html
├── relative_urls.css
├── @supports.html
├── unsupported-selectors.html
├── @layer.html
├── simple_link.html
├── cdn_link.html
├── cdn_link_iife.html
├── simple_style.html
├── parsing_error1.html
├── simple.html
├── layout_query.html
├── simple_new_syntax.html
├── simple_new_long_syntax.html
├── parsing_error2.html
├── dynamic.html
├── or.html
├── not.html
├── dynamic_container.html
├── dynamic_style.html
├── nested.html
├── index.html
├── and.html
├── dynamic_link.html
├── realworldstyles1.html
├── test-utils.js
├── relative_urls.html
├── runner.html
├── pseudo_element.html
├── named_shorthand.html
├── named.html
├── diff.ts
└── wpt.ts
├── .prettierrc.json
├── superstatic.json
├── .eslintrc.json
├── tsconfig.json
├── src
├── globals.d.ts
├── index.ts
├── utils
│ ├── ast.ts
│ ├── parse-media-query.ts
│ └── parse-media-feature.ts
├── constants.ts
├── wpt.ts
├── memo.ts
├── parser.ts
├── evaluate.ts
├── transform.ts
└── engine.ts
├── CHANGELOG.md
├── CONTRIBUTING.md
├── .github
└── workflows
│ ├── release-please.yml
│ ├── build_test.yml
│ └── update_baseline.yml
├── package.json
├── README.md
└── LICENSE
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist/
2 | wpt/
--------------------------------------------------------------------------------
/tests/red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/container-query-polyfill/HEAD/tests/red.png
--------------------------------------------------------------------------------
/tests/green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/container-query-polyfill/HEAD/tests/green.png
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": false,
3 | "singleQuote": true,
4 | "trailingComma": "es5",
5 | "arrowParens": "avoid"
6 | }
7 |
--------------------------------------------------------------------------------
/superstatic.json:
--------------------------------------------------------------------------------
1 | {
2 | "headers": [
3 | {
4 | "source": "**/*",
5 | "headers": [
6 | {
7 | "key": "Access-Control-Allow-Origin",
8 | "value": "*"
9 | }
10 | ]
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "env": {
4 | "browser": true,
5 | "es6": true,
6 | "node": true
7 | },
8 | "parser": "@typescript-eslint/parser",
9 | "plugins": ["@typescript-eslint"],
10 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"]
11 | }
12 |
--------------------------------------------------------------------------------
/tests/simple_link.css:
--------------------------------------------------------------------------------
1 | div {
2 | outline: 1px solid red;
3 | resize: both;
4 | overflow: auto;
5 | container-type: size;
6 | height: 100px;
7 | }
8 | h1 {
9 | font-size: 10px;
10 | }
11 | @container (min-width: 200px) {
12 | h1 {
13 | font-size: 30px;
14 | }
15 | }
16 | @container (max-width: 200px) {
17 | h1 {
18 | font-size: 20px;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": ".",
4 | "outDir": "dist",
5 | "lib": ["esnext", "DOM", "DOM.Iterable"],
6 | "strict": true,
7 | "target": "esnext",
8 | "module": "esnext",
9 | "moduleResolution": "node",
10 | "types": [],
11 | "esModuleInterop": true
12 | },
13 | "include": ["src/**/*.ts", "tests/**/*.ts"]
14 | }
15 |
--------------------------------------------------------------------------------
/tests/empty_href.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
--------------------------------------------------------------------------------
/tests/relative_urls.css:
--------------------------------------------------------------------------------
1 | div {
2 | outline: 1px solid red;
3 | resize: both;
4 | overflow: auto;
5 | container-type: size;
6 | height: 100px;
7 | display: grid;
8 | }
9 | #noquotes span {
10 | grid-area: 1/1;
11 | background: url(/tests/red.png);
12 | }
13 | #doublequotes span {
14 | grid-area: 1/1;
15 | background: url('/tests/red.png');
16 | }
17 | #singlequotes span {
18 | grid-area: 1/1;
19 | background: url('/tests/red.png');
20 | }
21 | @container (min-width: 200px) {
22 | #noquotes span {
23 | background: url(/tests/green.png);
24 | }
25 | #doublequotes span {
26 | background: url('/tests/green.png');
27 | }
28 | #singlequotes span {
29 | background: url('/tests/green.png');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/globals.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | declare const IS_WPT_BUILD: boolean;
15 | declare const PACKAGE_VERSION: string;
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [1.0.2](https://github.com/GoogleChromeLabs/container-query-polyfill/compare/v1.0.1...v1.0.2) (2022-11-08)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * improve :where supports check ([#65](https://github.com/GoogleChromeLabs/container-query-polyfill/issues/65)) ([1dab190](https://github.com/GoogleChromeLabs/container-query-polyfill/commit/1dab190dbd640f2ad1a1535c69a7143182729cee)), closes [#64](https://github.com/GoogleChromeLabs/container-query-polyfill/issues/64)
9 |
10 | ## [1.0.1](https://github.com/GoogleChromeLabs/container-query-polyfill/compare/v1.0.0...v1.0.1) (2022-10-19)
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * Improve performance on lower end devices ([48dfef8](https://github.com/GoogleChromeLabs/container-query-polyfill/commit/48dfef88f8eb037cd38ad8d43410950694504497))
16 |
--------------------------------------------------------------------------------
/tests/@supports.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
Ohai
21 |
22 |
35 |
--------------------------------------------------------------------------------
/tests/unsupported-selectors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
Ohai
21 |
22 |
35 |
--------------------------------------------------------------------------------
/tests/@layer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
22 |
Ohai
23 |
24 |
37 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.yml:
--------------------------------------------------------------------------------
1 | name: release-please
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | release-please:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: google-github-actions/release-please-action@v3
13 | id: release
14 | with:
15 | release-type: node
16 | package-name: container-query-polyfill
17 |
18 | - uses: actions/checkout@v3
19 | - uses: actions/setup-node@v3
20 | with:
21 | node-version: '16.x'
22 | if: ${{ steps.release.outputs.release_created }}
23 | - run: npm install
24 | if: ${{ steps.release.outputs.release_created }}
25 | - run: npm run build
26 | if: ${{ steps.release.outputs.release_created }}
27 |
28 | - uses: actions/setup-node@v3
29 | with:
30 | node-version: '16.x'
31 | registry-url: 'https://wombat-dressing-room.appspot.com/'
32 | if: ${{ steps.release.outputs.release_created }}
33 | - run: npm publish
34 | env:
35 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
36 | if: ${{ steps.release.outputs.release_created }}
37 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | import {initializePolyfill} from './engine';
15 | import {initializeForWPT, handleUpdate} from './wpt';
16 |
17 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
18 | (window as any).CQPolyfill = {
19 | version: PACKAGE_VERSION,
20 | };
21 |
22 | if (!('container' in document.documentElement.style)) {
23 | let updateCallback = () => {
24 | /* noop */
25 | };
26 | if (IS_WPT_BUILD) {
27 | initializeForWPT();
28 | updateCallback = handleUpdate;
29 | }
30 |
31 | initializePolyfill(updateCallback);
32 | }
33 |
--------------------------------------------------------------------------------
/tests/simple_link.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Ohai
5 |
6 |
41 |
--------------------------------------------------------------------------------
/tests/cdn_link.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Ohai
5 |
6 |
41 |
--------------------------------------------------------------------------------
/tests/cdn_link_iife.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Ohai
5 |
6 |
7 |
40 |
--------------------------------------------------------------------------------
/tests/simple_style.html:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
Ohai
26 |
27 |
51 |
--------------------------------------------------------------------------------
/tests/parsing_error1.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
Ohai
29 |
30 |
54 |
--------------------------------------------------------------------------------
/tests/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
Ohai
29 |
30 |
54 |
--------------------------------------------------------------------------------
/tests/layout_query.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
Ohai
29 |
30 |
54 |
--------------------------------------------------------------------------------
/tests/simple_new_syntax.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
Ohai
29 |
30 |
54 |
--------------------------------------------------------------------------------
/tests/simple_new_long_syntax.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
Ohai
29 |
30 |
54 |
--------------------------------------------------------------------------------
/tests/parsing_error2.html:
--------------------------------------------------------------------------------
1 |
2 |
29 |
30 |
Ohai
31 |
32 |
56 |
--------------------------------------------------------------------------------
/src/utils/ast.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | import {DeclarationNode, Node, Type} from './css';
15 |
16 | export function ws(): Node {
17 | return {type: Type.WhitespaceToken};
18 | }
19 |
20 | export function delim(value: string): Node {
21 | return {type: Type.DelimToken, value};
22 | }
23 |
24 | export function decl(name: string, value: Node[]): DeclarationNode {
25 | return {
26 | type: Type.DeclarationNode,
27 | name,
28 | value,
29 | important: false,
30 | };
31 | }
32 |
33 | export function ident(value: string): Node {
34 | return {type: Type.IdentToken, value};
35 | }
36 |
37 | export function func(name: string, value: Node[]): Node {
38 | return {type: Type.FunctionNode, name, value};
39 | }
40 |
41 | export function customVar(name: string) {
42 | return func('var', [ident(name)]);
43 | }
44 |
--------------------------------------------------------------------------------
/tests/dynamic.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
61 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | export const PER_RUN_UID = Array.from({length: 4}, () =>
15 | Math.floor(Math.random() * 256).toString(16)
16 | ).join('');
17 |
18 | export const INTERNAL_KEYWORD_PREFIX = 'cq-';
19 | export const CUSTOM_PROPERTY_SHORTHAND = getCustomVariableName('container');
20 | export const CUSTOM_PROPERTY_TYPE = getCustomVariableName('container-type');
21 | export const CUSTOM_PROPERTY_NAME = getCustomVariableName('container-name');
22 |
23 | export const DATA_ATTRIBUTE_SELF = `data-cqs-${PER_RUN_UID}`;
24 | export const DATA_ATTRIBUTE_CHILD = `data-cqc-${PER_RUN_UID}`;
25 |
26 | export const CUSTOM_UNIT_VARIABLE_CQW = getCustomVariableName('cqw');
27 | export const CUSTOM_UNIT_VARIABLE_CQH = getCustomVariableName('cqh');
28 | export const CUSTOM_UNIT_VARIABLE_CQI = getCustomVariableName('cqi');
29 | export const CUSTOM_UNIT_VARIABLE_CQB = getCustomVariableName('cqb');
30 |
31 | function getCustomVariableName(name: string): string {
32 | return `--cq-${name}-${PER_RUN_UID}`;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/or.html:
--------------------------------------------------------------------------------
1 |
2 |
21 |
26 |
51 |
--------------------------------------------------------------------------------
/tests/not.html:
--------------------------------------------------------------------------------
1 |
2 |
21 |
26 |
51 |
--------------------------------------------------------------------------------
/tests/dynamic_container.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
61 |
--------------------------------------------------------------------------------
/tests/dynamic_style.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "container-query-polyfill",
3 | "version": "1.0.2",
4 | "description": "",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/GoogleChromeLabs/container-query-polyfill.git"
8 | },
9 | "type": "module",
10 | "module": "dist/container-query-polyfill.modern.js",
11 | "unpkg": "dist/container-query-polyfill.modern.js",
12 | "publishConfig": {
13 | "source": "src/index.ts"
14 | },
15 | "exports": {
16 | "default": "./dist/container-query-polyfill.modern.js"
17 | },
18 | "files": [
19 | "README.md",
20 | "LICENSE",
21 | "dist/*.js",
22 | "package.json"
23 | ],
24 | "scripts": {
25 | "build:clean": "rimraf ./dist/",
26 | "build:wpt": "microbundle -f modern --no-compress --define PACKAGE_VERSION=${npm_package_version},IS_WPT_BUILD=1",
27 | "build": "microbundle -f modern --no-sourcemap --define PACKAGE_VERSION=${npm_package_version},IS_WPT_BUILD=0",
28 | "lint": "eslint '**/*.ts'",
29 | "prettier:fix": "prettier --write .",
30 | "serve": "superstatic -p 9606 .",
31 | "test": "node --loader ts-node/esm ./tests/wpt.ts"
32 | },
33 | "author": "Google Chrome Developers ",
34 | "contributors": [
35 | "Surma "
36 | ],
37 | "license": "Apache-2.0",
38 | "devDependencies": {
39 | "@types/async": "^3.2.14",
40 | "@types/selenium-webdriver": "^4.1.1",
41 | "@typescript-eslint/eslint-plugin": "^5.18.0",
42 | "@typescript-eslint/parser": "^5.18.0",
43 | "async": "^3.2.4",
44 | "browserstack-local": "^1.5.1",
45 | "eslint": "^8.12.0",
46 | "microbundle": "^0.15.0",
47 | "prettier": "^2.6.2",
48 | "rimraf": "^3.0.2",
49 | "selenium-webdriver": "^4.3.0",
50 | "superstatic": "^8.0.0",
51 | "ts-node": "^10.9.1"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/nested.html:
--------------------------------------------------------------------------------
1 |
2 |
29 |
34 |
61 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
72 |
--------------------------------------------------------------------------------
/tests/and.html:
--------------------------------------------------------------------------------
1 |
2 |
33 |
38 |
64 |
--------------------------------------------------------------------------------
/tests/dynamic_link.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
64 |
--------------------------------------------------------------------------------
/tests/realworldstyles1.html:
--------------------------------------------------------------------------------
1 |
2 |
44 |
45 |
Ohai
46 |
47 |
77 |
--------------------------------------------------------------------------------
/tests/test-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2021 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | export function doubleRaf() {
15 | return new Promise(resolve => {
16 | requestAnimationFrame(() => {
17 | requestAnimationFrame(() => {
18 | resolve();
19 | });
20 | });
21 | });
22 | }
23 |
24 | export function fail(msg) {
25 | window.parent?.postMessage(msg, '*');
26 | }
27 |
28 | export function success() {
29 | window.parent?.postMessage(true, '*');
30 | }
31 |
32 | export function nextEvent(el, name) {
33 | return new Promise(resolve =>
34 | el.addEventListener(name, resolve, {once: true})
35 | );
36 | }
37 |
38 | export function assert(bool, msg) {
39 | if (!bool) {
40 | throw Error(msg);
41 | }
42 | }
43 |
44 | export function assertEquals(a, b, msg) {
45 | if (a !== b) {
46 | throw Error(`Expected ${a} == ${b}. ${msg}`);
47 | }
48 | }
49 |
50 | export function timeout(ms) {
51 | return new Promise(resolve => setTimeout(resolve, ms));
52 | }
53 |
54 | export async function testSuite(name, cb) {
55 | try {
56 | await Promise.race([
57 | cb(),
58 | // timeout(2000).then(() => {
59 | // throw Error(`Timeout`);
60 | // }),
61 | ]);
62 | } catch (e) {
63 | console.error(e);
64 | fail(`${name}: ${e}`);
65 | return;
66 | }
67 | success();
68 | console.log('Test passed successfully');
69 | }
70 |
--------------------------------------------------------------------------------
/tests/relative_urls.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
61 |
--------------------------------------------------------------------------------
/.github/workflows/build_test.yml:
--------------------------------------------------------------------------------
1 | name: Build & Test
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | types: [opened, synchronize]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | env:
13 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
14 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
15 | WPT_MANIFEST: ${{ github.workspace }}/wpt/MANIFEST.json
16 | steps:
17 | - uses: actions/checkout@v3
18 | - uses: actions/setup-node@v3
19 | with:
20 | node-version: '16'
21 | - uses: actions/setup-python@v3
22 | with:
23 | python-version: '3.x'
24 | - uses: actions/checkout@v3
25 | with:
26 | repository: devknoll/wpt
27 | path: wpt
28 | ref: x-polyfill-all-tests
29 |
30 | - name: Build
31 | run: |
32 | npm install
33 | npm run build:wpt
34 |
35 | - name: Setup WPT
36 | run: |
37 | cd wpt
38 | pip install virtualenv
39 | ./wpt make-hosts-file | sudo tee -a /etc/hosts
40 | - name: Run Tests
41 | run: |
42 | npm run serve &
43 | ./wpt/wpt manifest
44 | ./wpt/wpt serve --inject-script=${{ github.workspace }}/dist/container-query-polyfill.modern.js &
45 | npm test
46 | ret=$(node --loader ts-node/esm ./tests/diff.ts)
47 | npm run prettier:fix
48 | cat ./tests/pr.txt >> $GITHUB_STEP_SUMMARY
49 | if [ $ret == "changed" ]; then
50 | exit 1
51 | fi
52 | - uses: actions/upload-artifact@v3
53 | with:
54 | name: baseline.json
55 | path: ./tests/baseline.json
56 | if: failure()
57 | - uses: actions/upload-artifact@v3
58 | with:
59 | name: results.json
60 | path: ./tests/results.json
61 | if: failure()
62 | - uses: actions/upload-artifact@v3
63 | with:
64 | name: raw.json
65 | path: ./tests/*.raw.json
66 | if: failure()
67 |
--------------------------------------------------------------------------------
/.github/workflows/update_baseline.yml:
--------------------------------------------------------------------------------
1 | name: Update WPT Baseline
2 |
3 | on:
4 | workflow_dispatch:
5 | branches: [main]
6 | schedule:
7 | - cron: '0 0 * * 1'
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | env:
13 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
14 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
15 | WPT_MANIFEST: ${{ github.workspace }}/wpt/MANIFEST.json
16 | SCHEDULED_BASELINE_DIFF: true
17 | steps:
18 | - uses: actions/checkout@v3
19 | - uses: actions/setup-node@v3
20 | with:
21 | node-version: '16'
22 | - uses: actions/setup-python@v3
23 | with:
24 | python-version: '3.x'
25 | - uses: actions/checkout@v3
26 | with:
27 | repository: devknoll/wpt
28 | path: wpt
29 | ref: x-polyfill-all-tests
30 |
31 | - name: Build
32 | run: |
33 | npm install
34 | npm run build:wpt
35 |
36 | - name: Setup WPT
37 | run: |
38 | cd wpt
39 | pip install virtualenv
40 | ./wpt make-hosts-file | sudo tee -a /etc/hosts
41 | - name: Run Tests
42 | run: |
43 | npm run serve &
44 | ./wpt/wpt manifest
45 | ./wpt/wpt serve --inject-script=${{ github.workspace }}/dist/container-query-polyfill.modern.js &
46 | npm test
47 |
48 | - name: Open Pull Request
49 | env:
50 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 | run: |
52 | ret=(node --loader ts-node/esm ./tests/diff.ts)
53 | if [ $ret == "changed" ]; then
54 | npm run prettier:fix
55 | git config user.name github-actions[bot]
56 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com
57 | git checkout -b update-wpt-baseline-$(date +"%Y-%m-%d")
58 | git add ./tests/baseline.json
59 | git commit -m "Update Web Platform Test baseline"
60 | git push -u origin HEAD
61 | gh pr create --title "[Automated] Update Web Platform Tests" --body-file ./tests/pr.txt --label "update-baseline"
62 | fi
63 |
--------------------------------------------------------------------------------
/src/wpt.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | let resolveFirstRender: (() => void) | null;
15 | const firstRenderPromise = new Promise(resolve => {
16 | resolveFirstRender = resolve;
17 | });
18 |
19 | export function handleUpdate() {
20 | if (resolveFirstRender) {
21 | resolveFirstRender();
22 | resolveFirstRender = null;
23 | }
24 | }
25 |
26 | export function initializeForWPT() {
27 | // HACK: Make WPT's testharnessreport.js think that we
28 | // are running in the default test environment, so that
29 | // we can configure it.
30 | if (!window.opener) {
31 | window.opener = window;
32 | }
33 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
34 | (window as any).testharness_properties = {
35 | output: true,
36 | timeout_multiplier: 100,
37 | };
38 |
39 | window.addEventListener('error', e => {
40 | e.stopImmediatePropagation();
41 | });
42 |
43 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
44 | (window as any).waitForPolyfill = async function () {
45 | await firstRenderPromise;
46 | return await new Promise(resolve => {
47 | requestAnimationFrame(() => {
48 | requestAnimationFrame(() => {
49 | requestAnimationFrame(() => {
50 | requestAnimationFrame(() => {
51 | requestAnimationFrame(() => {
52 | resolve();
53 | });
54 | });
55 | });
56 | });
57 | });
58 | });
59 | };
60 |
61 | const oldSupports = CSS.supports;
62 | CSS.supports = (ident: string) => {
63 | if (ident === 'container-type:size') {
64 | return true;
65 | }
66 | return oldSupports(ident);
67 | };
68 | }
69 |
--------------------------------------------------------------------------------
/tests/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/tests/pseudo_element.html:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
Ohai
32 |
33 |
108 |
--------------------------------------------------------------------------------
/src/memo.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google Inc. All Rights Reserved.
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | * Unless required by applicable law or agreed to in writing, software
8 | * distributed under the License is distributed on an "AS IS" BASIS,
9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | * See the License for the specific language governing permissions and
11 | * limitations under the License.
12 | */
13 |
14 | export class Reference {
15 | value: T;
16 |
17 | constructor(value: T) {
18 | this.value = value;
19 | }
20 | }
21 |
22 | export type MemoizableValue =
23 | | number
24 | | string
25 | | boolean
26 | | symbol
27 | | null
28 | | Reference
29 | | MemoizableValue[]
30 | | {[key: number | string | symbol]: MemoizableValue};
31 |
32 | export function memoizeAndReuse<
33 | TArgs extends MemoizableValue[],
34 | TResult extends MemoizableValue
35 | >(fn: (...args: TArgs) => TResult) {
36 | type Result = [TArgs, TResult];
37 | let previousResult: Result | null = null;
38 |
39 | return (...args: TArgs) => {
40 | if (previousResult == null || !areEqual(previousResult[0], args)) {
41 | const currentResult = fn(...args);
42 | if (
43 | previousResult == null ||
44 | !areEqual(previousResult[1], currentResult)
45 | ) {
46 | previousResult = [args, currentResult];
47 | }
48 | }
49 | return previousResult[1];
50 | };
51 | }
52 |
53 | function areEqual(lhs: MemoizableValue, rhs: MemoizableValue) {
54 | if (lhs === rhs) {
55 | return true;
56 | }
57 |
58 | if (typeof lhs === typeof rhs) {
59 | if (lhs !== null && rhs !== null && typeof lhs === 'object') {
60 | if (Array.isArray(lhs)) {
61 | if (!Array.isArray(rhs) || rhs.length !== lhs.length) {
62 | return false;
63 | }
64 |
65 | for (let i = 0, length = lhs.length; i < length; i++) {
66 | if (!areEqual(lhs[i], rhs[i])) {
67 | return false;
68 | }
69 | }
70 |
71 | return true;
72 | } else if (lhs instanceof Reference) {
73 | if (!(rhs instanceof Reference) || lhs.value !== rhs.value) {
74 | return false;
75 | }
76 | return true;
77 | } else {
78 | const leftKeys = Object.keys(lhs);
79 | if (leftKeys.length !== Object.keys(rhs).length) {
80 | return false;
81 | }
82 |
83 | for (let i = 0, length = leftKeys.length; i < length; i++) {
84 | const key = leftKeys[i];
85 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
86 | if (!areEqual(lhs[key], (rhs as any)[key])) {
87 | return false;
88 | }
89 | }
90 |
91 | return true;
92 | }
93 | }
94 | }
95 |
96 | return false;
97 | }
98 |
--------------------------------------------------------------------------------
/tests/named_shorthand.html:
--------------------------------------------------------------------------------
1 |
2 |
48 |
53 |
121 |
--------------------------------------------------------------------------------
/tests/named.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
51 |
56 |
123 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Container Query Polyfill
2 |
3 | *Please note that this polyfill is now in maintenance mode, as of Nov, 2022. We are not planning to add more features or enhancements.*
4 | ____________________________
5 |
6 | A small (9 kB compressed) polyfill for CSS Container Queries using [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) and [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) supporting the full [`@container`](https://drafts.csswg.org/css-contain-3/) query syntax:
7 |
8 | - Discrete queries (`width: 300` and `min-width: 300px`)
9 | - Range queries (`200px < width < 400px` and `width < 400px`)
10 | - Container relative length units (`cqw`, `cqh`, `cqi`, `cqb`, `cqmin`, and `cqmax`) in properties and keyframes
11 |
12 | ## Browser Support
13 |
14 | - Firefox 69+
15 | - Chrome 79+
16 | - Edge 79+
17 | - Safari 13.4+
18 |
19 | ## Getting Started
20 |
21 | ### Installation
22 |
23 | ```bash
24 | npm install --save container-query-polyfill
25 | ```
26 |
27 | Alternatively, you can use it directly from a CDN:
28 |
29 | ```js
30 |
31 | ```
32 |
33 | For the best user experience, it's recommended that you initially only use the polyfill for content below-the-fold and use `@supports` queries to temporarily replace it with a loading indicator until the polyfill is ready to display it:
34 |
35 | ```css
36 | @supports not (container-type: inline-size) {
37 | .container,
38 | footer {
39 | display: none;
40 | }
41 |
42 | .loader {
43 | display: flex;
44 | }
45 | }
46 | ```
47 |
48 | You can view a more complete demo [here](https://codesandbox.io/s/smoosh-glitter-m2ub4w?file=/index.html). On sufficiently fast networks and devices, or devices that natively support Container Queries, this loading indicator will never be displayed.
49 |
50 | > **Note**
51 | > Keep in mind that this technique effectively limits impact on FID and CLS, potentially at the expense of LCP. You may see regressions in the latter as a result, particularly on lower end devices or in poor network conditions.
52 |
53 | ## Limitations
54 |
55 | - **CSS first**: The polyfill currently only supports `