npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ember-container-query

v5.0.13

Published

Make container queries that harness the power of Ember

Downloads

1,242

Readme

This project uses GitHub Actions for continuous integration. This project is using Percy.io for visual regression testing.

ember-container-query

Make container queries that harness the power of Ember

Installation

ember install ember-container-query
  • Update your template registry to extend this addon's. Check the Glint documentation for more information.

    /* types/index.d.ts */
    
    import '@glint/environment-ember-loose';
    
    import type EmberContainerQueryRegistry from 'ember-container-query/template-registry';
    
    declare module '@glint/environment-ember-loose/registry' {
      export default interface Registry extends EmberContainerQueryRegistry, /* other addon registries */ {
        // local entries
      }
    }
  • If you are using <template> tag, you are good to go! Use the named import to consume things.

    /* app/components/tracks.{gjs,gts} */
    
    import { hash } from '@ember/helper';
    import { ContainerQuery, height, width } from 'ember-container-query';
    
    <template>
      <ContainerQuery
        @features={{hash
          small=(width max=480)
          medium=(width min=480 max=640)
          large=(width min=640)
          tall=(height min=320)
        }}
        as |CQ|
      >
        ...
      </ContainerQuery>
    </template>

Applications

Where can you use container queries? Here are real-life (and some theoretical) applications!

  1. Components form a core of an Ember app. We love components!

  2. With media queries,

    • A design that looked amazing on 2 or 3 fixed screen sizes can end up looking terrible at a size in-between.

    • Designing the template for specific screen sizes isn't a future-proof solution. You may need to reuse the component under different local width and height constraints.

    • In ember-qunit tests, the window is scaled by default. You may end up stubbing a service (fake the window size) to get certain DOM elements to (dis)appear.

  3. With container queries,

    • A component only needs to know how much space it has to figure out how to best present data.

    • Since each component can be free to decide how it looks, a webpage may end up with an unexpected combined look. This may be good, may be bad.

    • In tests, you will be driven to have a correct window size. If the window size is correct, then all elements should (dis)appear just like they would on your browser.

  1. A table is great for showing structured data. On mobile, with a limited width, not so much.

  2. You can use a list to show data vertically. This works until the user rotates the screen and sees only so much at a time.

  3. You can use container queries to decide which table columns to show and how many columns to spread the list across.

  1. It's difficult to create dashboard widgets that can be placed anywhere and look good.

  2. As a result, you may artificially constrain your users from customizing their dashboard.

  3. If you combine media and container queries, you can better meet the wants of designers, developers, and users.

  1. Currently, you have to use srcset to load images with the optimal file size. Alternatively, you can use container queries to decide which images to load.

  2. Similarly, for videos, you could use container queries to decide format and display resolution.

  3. D3 components can use container queries to decide what's the best way to show data. Do you show larger chart elements, show legends, allow scrolling, show text summary? etc.

  1. You may be able to compose this addon with others to arrive at something ambitious and unique.

  2. For example, ember-printable-pages lets us reuse components to make a printable document. The components could focus on presenting data with container queries, while ember-printable-pages could focus on deciding page layouts and lazily rendering components.

  1. You're designing a game in Ember.

  2. Maybe a secret, most powerful item appears when the game world is at a certain size? :)

Not convinced? Open the demo app to see ember-container-query in action. (There's even a 404 page!)

API

ember-container-query provides 1 Glimmer component and 3 helpers:

  • <ContainerQuery>
  • {{aspect-ratio}}
  • {{height}}
  • {{width}}

The addon also provides a modifier so that you can opt out of using the provided component. You may also use the modifier to get the container dimensions when the window is resized.

  • {{container-query}}

Expand the items below to learn more about the API.

Arguments

You can pass these arguments to the component.

| Name | Required | Description | Type | |--|:--:|--|--| | @features | Yes1 | Container query definitions | POJO | | @dataAttributePrefix | No | Prefix for data attributes | string | | @debounce | No | Debounce time for resize (ms) | number ≥ 0 | | @tagName | No | Container tag name2 | HTML tag name |

1. The component renders without error when @features isn't provided. In practice, you will always want to set @features.

2. By default, the component is a <div> element. You can pass a valid HTML tag name to facilitate accessibility and semantic HTML.

Attributes

You may1 pass attributes to the component for these reasons:

  • Style (e.g. class, local-class)
  • Accessibility (e.g. ARIA attributes2, roles)

1. Do refrain from overusing splattributes (e.g. pass a {{did-insert}} modifier to fetch data), since the component's API may change and cause unexpected results. Practice separation of concerns when possible. For example, data fetching can be handled by another element or @use decorator.

2. When an ARIA attribute has multiple values, the order of values can matter. At the moment, splattributes doesn't guarantee the order.

Outputs

You can consume these values in your app or addon.

| Name | Yielded | Description | Type | |--|:--:|--|--| | features | Yes | Container query results | POJO | | dimensions | Yes | Container dimensions | POJO | | data-container-query-{featureName} | No | Data attributes for CSS selector | HTML data attribute |

Arguments

All helpers accept these arguments:

| Name | Required | Description | Type | |--|:--:|--|--| | min | Yes1 | Lower bound for feature2 | number ≥ 0 | | max | Yes1 | Upper bound for feature2 | number ≥ 0 |

1. The helpers use default values of min = 0 and max = Infinity, and assume the inequalities min ≤ x < max. In practice, you will always want to set min or max (or both).

2. Aspect ratio is unitless. Height and width have the unit of pixel.

Arguments

You can pass these arguments to the modifier.

  • @dataAttributePrefix
  • @debounce
  • @features
  • @onQuery

For more information, refer to the arguments of <ContainerQuery> component.

Outputs

The outputs are similar to those of <ContainerQuery> component.

Data attributes are automatically applied to the HTML element. To get dimensions and features, you will need to pass the argument @onQuery (a function) to the modifier.

/* app/components/chart.gts */

import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { containerQuery, type Dimensions } from 'ember-container-query';

export default class ChartComponent extends Component {
  @tracked height!: number;
  @tracked width!: number;

  @action updateDimensions({ dimensions }: { dimensions: Dimensions }) {
    const { height, width } = dimensions;

    this.height = height;
    this.width = width;
  }

  // ...

  <template>
    <div {{containerQuery onQuery=this.updateDimensions}}>
      <svg></svg>
    </div>
  </template>
}

Example

Let's look at the code that created the video demo above.

<ContainerQuery
  @features={{hash
    large=(width min=960)
    tall=(height min=400)
  }}
  as |CQ|
>
  {{#let
    (and CQ.features.large CQ.features.tall)
    as |showLyrics|
  }}
    <section local-class="container {{if showLyrics "with-lyrics"}}">
      <header local-class="album-header">
        <h1>{{@model.name}}</h1>
        <p>by <strong>{{@model.band.name}}</strong></p>
      </header>

      <div local-class="album-tracks">
        <Tracks
          @tracks={{@model.tracks}}
        />
      </div>

      {{#if showLyrics}}
        <div local-class="track-lyrics" tabindex="0">
          <Lyrics
            @lyrics={{this.currentTrack.lyrics}}
          />
        </div>
      {{/if}}
    </section>
  {{/let}}
</ContainerQuery>
<ContainerQuery
  @features={{hash
    small=(width max=480)
    medium=(width min=480 max=640)
    large=(width min=640)
    tall=(height min=320)
  }}
  as |CQ|
>
  {{#if (and CQ.features.large CQ.features.tall)}}
    <Tracks::Table
      @tracks={{@tracks}}
    />

  {{else}}
    <Tracks::List
      @numColumns={{
        if CQ.features.small 1
        (if CQ.features.medium 2 3)
      }}
      @tracks={{@tracks}}
    />

  {{/if}}
</ContainerQuery>

You can see that the album page uses 2 <ContainerQuery> components. Rest assured, they act independently of each other. When you pair <ContainerQuery> with some CSS, you can create layouts beyond the dreams of others! 🙌

For more examples, I encourage you to check out the code for my demo app. It is located under the docs-app folder and is structured like a typical Ember app.

Compatibility

  • Ember.js v4.12 or above1
  • Node.js v18 or above

1. ember-container-query may work on older versions of Ember (e.g. 4.4), but issues that arise from these won't be supported.

Contributing

See the Contributing guide for details.

Credits

Much thanks goes to Chad Carbert (@chadian), who introduced me to container queries at EmberFest 2019 and created ember-fill-up 🌟. I modeled the API for ember-container-query based on Chad's addon.

License

This project is licensed under the MIT License.