Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add FluentCard
  • Loading branch information
dvoituron committed Jun 2, 2025
commit 35234f03f8efd6f7133d44ca197673abe9a46560
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<FluentStack VerticalGap="12px" Style="max-width: 500px;"
Orientation="Orientation.Vertical">

<b>Default</b>
<FluentCard>
This is the default style to use for cards. Use this style variant for most of your card designs.
</FluentCard>

<br />

<b>Filled</b>
<FluentCard Appearance="@CardAppearance.Filled">
Use if your card is being displayed on a lighter gray or white surface. <br />
This ensures that you have adequate contrast between the card surface and the background of the application.
</FluentCard>

<br />

<b>Outline</b>
<FluentCard Appearance="@CardAppearance.Outline">
Use when you don't want a filled background color but a discernable outline (border) on the card.
</FluentCard>

<br />

<b>Subtle</b>
<FluentCard Appearance="@CardAppearance.Subtle">
This variant doesn't have a background or border for the card container.<br />
However, it does include interaction states that display a visible footprint when interacting with the card item.
</FluentCard>
</FluentStack>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<FluentCard OnClick="@CardClick">
This FluentCard is clickable.
</FluentCard>

@code
{
void CardClick()
{
Console.WriteLine("Card clicked");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: Card
route: /Card
---

# Card

A **FluentCard** is a container that holds information and actions related to a single concept or object, like a document or a contact.

Cards can give information prominence and create predictable patterns.
While they're very flexible, it's important to use them consistently for particular use cases across experiences.

By default, each card is of `role="group"`.

## Appearance

Cards can have different styles depending on the situation and where it is placed.

{{ CardAppearanceExample }}


## Clickable

Adding a `OnClick` handler to a card will make it clickable, which is useful for navigation or actions.

{{ CardClickable }}
16 changes: 16 additions & 0 deletions src/Core/Components/Card/FluentCard.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
@using Microsoft.FluentUI.AspNetCore.Components.Extensions
@inherits FluentComponentBase

<div id="@Id"
appearance="@Appearance.ToAttributeValue(CardAppearance.Default, returnEmptyAsNull: true)"
class="@ClassValue"
style="@StyleValue"
role="@Role"
selectable="@OnClick.HasDelegate"
tabindex="@(OnClick.HasDelegate ? 0 : null)"
@onclick="@ClickHandlerAsync"
@onkeydown="KeyDownHandlerAsync"
@attributes="@AdditionalAttributes">
@ChildContent
</div>
80 changes: 80 additions & 0 deletions src/Core/Components/Card/FluentCard.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// A FluentCard is a container that holds information and actions related to a single concept or object, like a document or a contact.
/// </summary>
public partial class FluentCard : FluentComponentBase
{
/// <summary />
protected string? ClassValue => DefaultClassBuilder
.AddClass("fluent-card")
.Build();

/// <summary />
protected string? StyleValue => DefaultStyleBuilder
.AddStyle("width", string.IsNullOrEmpty(Width) ? "100%" : Width)
.AddStyle("height", string.IsNullOrEmpty(Height) ? "fit-content" : Height)
.Build();

/// <summary>
/// Gets or sets the content of the card.
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets or sets the appearance of the component.
/// </summary>
[Parameter]
public CardAppearance Appearance { get; set; }

/// <summary>
/// Gets or sets the width of the component.
/// </summary>
[Parameter]
public string? Width { get; set; }

/// <summary>
/// Gets or sets the height of the component.
/// </summary>
[Parameter]
public string? Height { get; set; }

/// <summary>
/// Command executed when the user clicks on the card.
/// </summary>
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }

/// <summary>
/// Gets or sets the role of the card.
/// </summary>
[Parameter]
public string Role { get; set; } = "group";

/// <summary />
internal async Task ClickHandlerAsync(MouseEventArgs args)
{
if (OnClick.HasDelegate)
{
await OnClick.InvokeAsync(args);
}
}

/// <summary />
internal async Task KeyDownHandlerAsync(KeyboardEventArgs args)
{
if (string.Equals(args.Key, "Enter", StringComparison.OrdinalIgnoreCase) ||
string.Equals(args.Key, " ", StringComparison.OrdinalIgnoreCase))
{
await ClickHandlerAsync(new MouseEventArgs { ClientX = 0, ClientY = 0 });
}
}
}
43 changes: 43 additions & 0 deletions src/Core/Components/Card/FluentCard.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.fluent-card {
color: var(--colorNeutralForeground1);
padding: var(--spacingVerticalM) var(--spacingHorizontalM);
}

.fluent-card:not([appearance]),
.fluent-card[appearance="default"] {
background-color: var(--colorNeutralBackground1);
border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
border-radius: var(--borderRadiusMedium);
box-shadow: var(--shadow4);
}

.fluent-card[appearance="filled"] {
background-color: var(--colorNeutralBackground2);
border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
border-radius: var(--borderRadiusMedium);
box-shadow: var(--shadow4);
}

.fluent-card[appearance="outline"] {
background-color: var(--colorTransparentBackground);
border: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
border-radius: var(--borderRadiusMedium);
}

.fluent-card[appearance="subtle"] {
}

/* Hover */

.fluent-card[selectable]:hover {
background-color: var(--colorNeutralBackground1Hover);
color: var(--colorNeutralForeground1Hover);
border-color: var(--colorNeutralStroke1Hover);
cursor: pointer;
}

.fluent-card[selectable]:not([appearance]):hover,
.fluent-card[selectable][appearance="default"]:hover,
.fluent-card[selectable][appearance="filled"]:hover {
box-shadow: var(--shadow8);
}
37 changes: 37 additions & 0 deletions src/Core/Enums/CardAppearance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using System.ComponentModel;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// The visual appearance of the <see cref="FluentCard" />.
/// </summary>
public enum CardAppearance
{
/// <summary>
/// The card appears with the default style.
/// </summary>
[Description("default")]
Default,

/// <summary>
/// The card is being displayed on a lighter gray or white surface.
/// </summary>
[Description("filled")]
Filled,

/// <summary>
/// Use when you don't want a filled background color but a discernable outline (border) on the card.
/// </summary>
[Description("outline")]
Outline,

/// <summary>
/// This variant doesn't have a background or border for the card container.
/// </summary>
[Description("subtle")]
Subtle,
}