Cards

Cards are essentialy flexbox-based boxes that take up their container's width. These cards are set up as media query-less responsive cards that leverage the behavior of flex-wrap. As such, it's up to you to set an the flex-basis properties so that your content will stack (aka wrap) when the viewport cannot accomodate your content otherwise.

Examples

Border Card
The card comes with minimal skinning css and no padding (the padding you see here is from the demo styles). By default Cards have a flex direction of row, so each child with a flex rule will get placed as a sort of column in the row (until the viewport is shrunk below a size that can support the content's flex-basis; under that it will wrap and thus stack).
Border & Rounded
The card comes with minimal skinning css and no padding (the padding you see here is from the demo styles). By default Cards have a flex direction of row, so each child with a flex rule will get placed as a sort of column in the row (until the viewport is shrunk below a size that can support the content's flex-basis; under that it will wrap and thus stack).
Stacked & Border
Stacked cards start their lives with flex direction column, so each child be stacked one on top of the other and continue to grow downward.
Stacked & Shadow
Card
Stacked, Animated (on hover), & Shadow
Card
Success
Card
Info
Card
Warning
Card
Error
Card

Usage

astro logo

React, Vue, and Svelte examples on a single playground page 🚀 💥

react logoReact

View source
import 'agnostic-react/dist/common.min.css';
import 'agnostic-react/dist/esm/index.css';
import { Card } from 'agnostic-react';

export const YourComponent = () => (
  <section>
    <Card isBorder>
      <>
        <div className="p16">Border Card</div>
        <div className="p16 flex-grow-1 flex-shrink-1" style={{flexBasis: "50ch"}}>
          The card comes with minimal
          <span class="quoted">skinning css</span>
          and no padding (the padding you see here is from the demo styles).
          By default Cards have a flex direction of <i>row</i>, so each child
          with a <i>flex</i> rule will get placed as a sort of column
          in the row (until the viewport is shrunk below a size that can support
          the content's flex-basis; under that it will wrap and thus stack).
        </div>
      </>
    </Card>
    <div className="mbe24" />
    <Card isBorder isRounded>
      <>
        <div className="p16">Border & Rounded</div>
        <div className="p16 flex-grow-1 flex-shrink-1" style={{flexBasis: "50ch"}}>
          The card comes with minimal
          <span class="quoted">skinning css</span>
          and no padding (the padding you see here is from the demo styles).
          By default Cards have a flex direction of <i>row</i>, so each child
          with a <i>flex</i> rule will get placed as a sort of column
          in the row (until the viewport is shrunk below a size that can support
          the content's flex-basis; under that it will wrap and thus stack).
        </div>
      </>
    </Card>
    <div className="mbe24" />
    <Card isBorder isRounded>
      <>
        <div class="p16">Rounded with border</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card isStacked isBorder>
      <>
        <div class="p16">Stacked (direction column)</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card isStacked isShadow>
      <>
        <div class="p16">Stacked with shadow</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card isAnimated isShadow isStacked>
      <>
        <div class="p16">Animated, stacked, with shadow</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card type="success" isStacked>
      <>
        <div class="p16">Success</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card type="success" isRounded isStacked>
      <>
        <div class="p16">Success rounded</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card type="info" isStacked>
      <>
        <div class="p16">Info</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card type="warning" isStacked>
      <>
        <div class="p16">Warning</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card type="error" isStacked>
      <>
        <div class="p16">Error</div>
        <div class="p16">Card</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card isSkinned={false}>
      <>
        <div class="p16">Base Card</div>
        <div class="p16">No Skin</div>
      </>
    </Card>
    <div className="mbe24" />
    <Card css="foo-bar">
      <>
        <div class="p16">Custom CSS Class</div>
        <div class="p16">Inspect to see the class: foo-bar</div>
      </>
    </Card>
  </section>
);

React: component source, storybook tests

Vue 3 logoVue 3

View source
<template>
  <section>
    <Card is-border>
      <div class="p16">Border Card</div>
      <div
        class="p16 flex-grow-1 flex-shrink-1"
        style="flex-basis: 50ch;"
      >
        The card comes with minimal
        <span class="quoted">skinning css</span>
        and no padding (the padding you see here is from the demo styles).
        By default Cards have a flex direction of <i>row</i>, so each child
        with a <i>flex</i> rule will get placed as a sort of column
        in the row (until the viewport is shrunk below a size that can support
        the content's flex-basis; under that it will wrap and thus stack).
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      is-border
      is-rounded
    >
      <div class="p16">
        Border & Rounded
      </div>
      <div
        class="p16 flex-grow-1 flex-shrink-1"
        style="flex-basis: 50ch;"
      >
        The card comes with minimal
        <span class="quoted">skinning css</span>
        and no padding (the padding you see here is from the demo styles).
        By default Cards have a flex direction of <i>row</i>, so each child
        with a <i>flex</i> rule will get placed as a sort of column
        in the row (until the viewport is shrunk below a size that can support
        the content's flex-basis; under that it will wrap and thus stack).
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      is-stacked
      is-border
    >
      <div class="p16">
        Stacked & Border
      </div>
      <div class="p16">
        Stacked cards start their lives with flex direction <i>column</i>, so each child
        be stacked one on top of the other and continue to grow downward.
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      is-stacked
      is-shadow
    >
      <div class="p16">
        Stacked & Shadow
      </div>
      <div class="p16">
        Card
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      is-stacked
      is-shadow
      is-animated
    >
      <div class="p16">
        Stacked, Animated (on hover), & Shadow
      </div>
      <div class="p16">
        Card
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      type="success"
      is-stacked
    >
      <div class="p16">
        Success
      </div>
      <div class="p16">
        Card
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      type="info"
      is-stacked
    >
      <div class="p16">
        Info
      </div>
      <div class="p16">
        Card
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      type="warning"
      is-stacked
    >
      <div class="p16">
        Warning
      </div>
      <div class="p16">
        Card
      </div>
    </Card>
    <div class="mbe24" />
    <Card
      type="error"
      is-stacked
    >
      <div class="p16">
        Error
      </div>
      <div class="p16">
        Card
      </div>
    </Card> 
  </section>
</template>
<script setup>
// Import AgnosticUI global common & component CSS
import "agnostic-vue/dist/common.min.css";
import "agnostic-vue/dist/index.css";
import { Card } from "agnostic-vue";
</script>

Vue 3: component source, storybook tests

Svelte logoSvelte

View source
<script>
  import 'agnostic-svelte/css/common.min.css';
  import { Card } from "agnostic-svelte";
</script>

<section>
  <Card isBorder="{true}">
    <div class="p16">Border Card</div>
    <div class="p16 flex-grow-1 flex-shrink-1"
         style="flex-basis: 50ch;">
      The card comes with minimal
      <span class="quoted">skinning css</span>
      and no padding (the padding you see here is from the demo styles).
      By default Cards have a flex direction of <i>row</i>, so each child
      with a <i>flex</i> rule will get placed as a sort of column
      in the row (until the viewport is shrunk below a size that can support
      the content's flex-basis; under that it will wrap and thus stack).
    </div>
  </Card>
  <Card isBorder="{true}" isRounded="{true}">
    <div class="p16">
      Border & Rounded
    </div>
    <div class="p16 flex-grow-1 flex-shrink-1"
         style="flex-basis: 50ch;">
      The card comes with minimal
      <span class="quoted">skinning css</span>
      and no padding (the padding you see here is from the demo styles).
      By default Cards have a flex direction of <i>row</i>, so each child
      with a <i>flex</i> rule will get placed as a sort of column
      in the row (until the viewport is shrunk below a size that can support
      the content's flex-basis; under that it will wrap and thus stack).
    </div>
  </Card>
  <Card isBorder="{true}" isStacked="{true}">
    <div class="p16">
      Stacked & Border
    </div>
    <div class="p16">
      Stacked cards start their lives with flex direction <i>column</i>, so each child
      be stacked one on top of the other and continue to grow downward.
    </div>
  </Card>
  <Card isShadow="{true}" isStacked="{true}">
    <div class="p16">Stacked and shadow</div>
    <div class="p16">Card</div>
  </Card>
  <Card isStacked="{true}" isShadow="{true}" isAnimated="{true}">
    <div class="p16">Stacked, shadown, and animated</div>
    <div class="p16">Card</div>
  </Card>
  <Card type="success" isStacked="{true}">
    <div class="custom-wrap">
      <div class="p16">Success stacked</div>
      <div class="p16">Card</div>
    </div>
  </Card>
  <Card type="info" isStacked="{true}">
    <div class="p16">Info and stacked</div>
    <div class="p16">Card</div>
  </Card>
  <Card type="warning" isStacked="{true}">
    <div class="p16">Warning and stacked</div>
    <div class="p16">Card</div>
  </Card>
  <Card type="error" isStacked="{true}">
    <div class="p16">Error and stacked</div>
    <div class="p16">Card</div>
  </Card>
</section>

Svelte: component source, storybook tests

Angular logoAngular (Experimental)

View source

In your Angular configuration (likely angular.json) ensure you're including the common AgnosticUI styles:

"styles": ["agnostic-angular/common.min.css"],

Add AgnosticUI's AgModule module:



 





 





import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AgModule } from 'agnostic-angular';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AgModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Now you can use in your components:

import { Component } from '@angular/core';

@Component({
  selector: 'your-component',
  template: `<section>
    <h2>Cards</h2>
    <ag-card [isBorder]="true">
      <div class="p16">Border Card</div>
      <div class="p16 flex-grow-1 flex-shrink-1"
           style="flex-basis: 50ch;">
        The card comes with minimal
        <span class="quoted">skinning css</span>
        and no padding (the padding you see here is from the demo styles).
        By default Cards have a flex direction of <i>row</i>, so each child
        with a <i>flex</i> rule will get placed as a sort of column
        in the row (until the viewport is shrunk below a size that can support
        the content's flex-basis; under that it will wrap and thus stack).
      </div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card [isBorder]="true"
             [isRounded]="true">
      <div class="p16">
        Border & Rounded
      </div>
      <div class="p16 flex-grow-1 flex-shrink-1"
           style="flex-basis: 50ch;">
        The card comes with minimal
        <span class="quoted">skinning css</span>
        and no padding (the padding you see here is from the demo styles).
        By default Cards have a flex direction of <i>row</i>, so each child
        with a <i>flex</i> rule will get placed as a sort of column
        in the row (until the viewport is shrunk below a size that can support
        the content's flex-basis; under that it will wrap and thus stack).
      </div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card [isStacked]="true"
             [isBorder]="true">
      <div class="p16">
        Stacked & Border
      </div>
      <div class="p16">
        Stacked cards start their lives with flex direction <i>column</i>, so each child
        be stacked one on top of the other and continue to grow downward.
      </div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card [isStacked]="true"
             [isShadow]="true">
      <div class="p16">Stacked and shadow</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card [isStacked]="true"
             [isShadow]="true"
             [isAnimated]="true">
      <div class="p16">Stacked, shadown, and animated</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card type="success"
             [isStacked]="true">
      <div class="p16">Success stacked</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card type="success"
             [isStacked]="true"
             [isRounded]="true">
      <div class="p16">Success, stacked, and rounded</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card type="info"
             [isStacked]="true">
      <div class="p16">Info and stacked</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card type="warning"
             [isStacked]="true">
      <div class="p16">Warning and stacked</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card type="error"
             [isStacked]="true">
      <div class="p16">Error and stacked</div>
      <div class="p16">Card</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card [isSkinned]="false">
      <div class="p16">Base Card</div>
      <div class="p16">No Skin</div>
    </ag-card>
    <div class="mbe24"></div>
    <ag-card css="addition-classes">
      <div class="p16">Custom CSS Class Overrides</div>
      <div class="p16">Inspect to see additional-classes</div>
    </ag-card>
  </section>`
})
export class YourComponent { //... }

Angular: component source, storybook tests

A Note on Customizing the Animation

If you pass isAnimated, the underlying CSS applied will animate the card upwards on hover (or touch on mobile) by default. You can, however, override this by passing a CSS class of your own to the css prop, and then overriding the transform. For example, to have the transformation animate downwards (instead of upwards):

/* You may have to apply extra classes or other specificity shenanigans
depending on the framework and if it increases the specificity. */
.card.card-animated.animation-override:hover {
  /* animate down Y instead of up */
  transform: translateY(3px);
}

Interestingly, we noticed on Vue 3, the style obfuscation is applied to the original class ._card-animated_aihsb_47. This is still just one CSS class though! So, we were able to add our override without any extra classess. Here's the Vue 3 version of the consuming component's code:

<style>
.animation-override:hover {
  transform: translateY(3px);
}
</style>

And

  <Card
    isAnimated
    css="animation-override"
  >
    <div>...shortened for brevity</div>
  </Card>

And then you'd pass css="animation-override" to the Card component. See examples/src/styles.css in the Angular example application that this example is pulled from.

Customizations via CSS Properties

If you'd like to further customize the look of Card, there are several CSS custom properties you can override — see card.css and simply override those in your codebase after including AgnosticUI.

If you just want a container with no skinning styles whatsoever, you can also just pass isSkinned as false.

Storybook

You can run the framework Storybooks and see live examples for React, Vue 3, Svelte, Astro, and Angular (experimental). The following will set up Storybook and run locally:

How to run Storybook
git clone git@github.com:AgnosticUI/agnosticui.git
cd agnosticui/<PACKAGE_NAME> && npm i # e.g. cd agnosticui/agnostic-react && npm i
npm run storybook

See Running Storybook.