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

@micheldever/qwik-compose

v1.1.0

Published

A styled-components-like API for creating stylish Qwik primitives

Downloads

62

Readme

Installation

This package is distributed via npm. You can install it as a dependency in your project by running:

yarn add @micheldever/qwik-compose

Usage

To create a composed component, access one of the element properties from the compose function and pass an array of class names to apply:

import { compose } from '@micheldever/qwik-compose';

// Create a <Wrapper> Qwik component that implements the following
// Tailwind CSS classes and renders as a <section> html element
const Wrapper = compose.section([
  'bg-orange-100',
  'p-16',
]);

// Create a <Title> Qwik component that implements the following
// Tailwind CSS classes and renders as a <h1> html element
const Title = compose.h1([
  'text-center',
  'text-2xl',
  'text-rose-400',
  'font-serif',
  'font-bold',
  'my-4',
]);

// Use them like regular Qwik components – except they're styled!
function Application() {
  return (
    <Wrapper>
      <Title>Hello World, this is my first composed component!</Title>
    </Wrapper>
  );
}

Transient Props

Since @micheldever/qwik-compose does not execute any runtime code, applying conditional styling can be tricky. As such, it is recommended that you lean on data attributes to use as hooks for your conditional styles.

To make this pattern quick and easy to manage, you can prefix your conditional prop names with a dollar sign ($) to turn them into transient props. This will then convert your prop into a data attribute pre-runtime. For example, a prop named $isLarge is not rendered to the final DOM element, but instead is transformed to be data-is-large which can then be used as a styling hook.

This functionality is entirely optional and you can still directly pass data attributes if that makes more sense to you.

const Button = compose.button([
  'bg-red-500',
  'text-red-50',
  'data-[is-large="true"]:text-xl',
]);

// outputs <button class="bg-red-500 text-red-50 data-[is-large="true"]:text-xl" data-is-large="true">
<Button $isLarge={true}>

Extending Components

As you start to build out your component library, you may wish to use a component but slightly change or build upon its styling. While you could use the transient props pattern above, depending on how many alterations you make, this could quickly become unmaintainable.

To simplify the process, you can extend an existing composed component and supply it with a list of additional classes you wish to attach, resulting in a new component that is the best of both worlds.

// outputs <button class="bg-red-500 text-red-50">
const Button = compose.button([
  'bg-red-500',
  'text-red-50',
]);

// outputs <button class="bg-red-500 text-red-50 border border-red-700">
const BorderedButton = compose(Button, [
  'border',
  'border-red-700'
]);

This also works for custom components, as long as they pass the class prop to a DOM element.

function CustomButton({ children, class }) {
  return <button class={class}>{children}</button>;
}

const ComposedCustomButton = composed(CustomButton, [
  'bg-red-500',
  'text-red-50',
]);

// outputs <button class="bg-red-500 text-red-50">
<ComposedCustomButton />

You can also pass extra classes to individual component instances via the class prop.

// outputs <button class="bg-red-500 text-red-50 border border-red-700">
<Button class='border border-red-700' />

Using as

All composed components are polymorphic, meaning you are able to alter the way they render after they have been created by using the as prop. This keeps all the styling that has been applied to a component but just switches out what is ultimately being rendered (be it a different HTML element or a different custom component).

const Button = compose.button([ ... ]);

// This component will render as a `div` element instead of a `button`
<Button as='div' />

Using attrs

Occasionally, you may know ahead of time if your component will always use the same static prop values, such as an input element having a set type property. By using the attrs method, you can implicitly set any static prop values that should be passed down to every instance of your component.

Furthermore, you can also use the attrs method to attach default values for your dynamic transient props.

const TextField = compose.input([ ... ]).attrs({ $hasIcon: false, type: 'text' });

// This will render with the `type` attribute implicitly set
// from the original declaration
<TextField />

// You can also locally override any attributes that are defined above
<TextField $hasIcon={true} type='email' />
// For extended components, you can define attributes in the same way
const EmailField = styled(TextField, [ ... ]).attrs({ type: 'email' });

Using toClass

If you just need the output class string and not the created Qwik component, you can call the toClass method on any composed component to receive the concatenated class.

// outputs <button class="bg-red-500 text-red-50">
const Button = compose.button([
  'bg-red-500',
  'text-red-50',
]);

// outputs bg-red-500 text-red-50
const className = Button.toClass();

Execution Hooks

@micheldever/qwik-compose allows you to perform additional custom logic to the concatenated class string by defining a global event handler for the onDone event. This can be particularly useful if you want to combine @micheldever/qwik-compose with a utility function such as tailwind-merge.

Example with tailwind-merge

// qwik-compose.config.ts

import { defineConfig } from '@micheldever/qwik-compose';
import { twMerge } from 'tailwind-merge';
import type { DefineConfigOptions } from '@micheldever/qwik-compose';


const config: DefineConfigOptions = {
  hooks: {
    onDone: (className) => twMerge(className),
  },
};

export const { compose } = defineConfig(config);
// components/button.ts

import { compose } from '../qwik-compose.config';

const Button = compose.button([
  'bg-orange-100',
  'bg-red-500',
]);

// outputs <button class="bg-red-500">
<Button />