Files
IGP-Fan-Reference/docs/cooldown-button.md

4.6 KiB
Raw Permalink Blame History

Cooldown Button Component

A square Blazor button with a 12-second cooldown animation: when clicked, the button greys out and a circular transparency wedge dials open clockwise, progressively revealing the normal button state underneath.


Visual Design

The button has two visual states:

Idle state A solid square button with the project's standard --paper background and --primary border. Hover inverts or brightens the paper. Click triggers a brief scale-down.

Cooldown state The native button fades to opacity: 0 while an absolutely-positioned overlay covers it. The overlay uses a conic-gradient CSS mask to create a "dialling open" effect. A number in the centre shows the remaining seconds.


Core Technique: Conic-Gradient Mask

The cooldown reveal is accomplished with a conic-gradient mask applied to the overlay <div>:

mask-image: conic-gradient(
    transparent 0deg,
    transparent {angle}deg,
    #000        {angle}deg,
    #000        360deg
);
  • transparent lets the button underneath show through (revealed area)
  • #000 (black) fully masks the overlay, making it visible (greyed-out area)

At angle = 0deg, transparent covers nothing and the mask is entirely black → the overlay is fully opaque (button completely greyed out).

At angle = 360deg, transparent covers the full circle and black covers nothing → the overlay is fully transparent (button completely visible).

The angle animates linearly from 0 to 360 over the cooldown duration. Because conic-gradient starts at the 12 o'clock position and sweeps clockwise, the reveal begins at the top of the button and rotates around, like a clock hand or a dial opening.


Implementation Architecture

Component Parameters

Parameter Type Default Description
ChildContent RenderFragment null Text or content inside the button
OnClick EventCallback Fired when the button is clicked
CooldownSeconds int 12 Duration of the cooldown in seconds
Size int 120 Width and height in pixels (square)

Timer Loop

A System.Timers.Timer fires every ~33ms (≈30fps) during the cooldown:

OnTick:
    elapsed = UtcNow - startTime
    if elapsed >= CooldownSeconds → end cooldown, dispose timer
    _elapsedAngle   = (elapsed / CooldownSeconds) * 360
    _remainingSeconds = CooldownSeconds - (int)elapsed
    InvokeAsync(StateHasChanged)

On each tick, _elapsedAngle is written into the overlay's inline style attribute, causing Blazor to re-render the mask-image. The timer is disposed in Dispose() to prevent leaks.

Disposal

The component implements IDisposable to clean up the timer when the component is removed from the render tree. This follows the same pattern used by SearchDialogComponent and BuildChartComponent elsewhere in the codebase.


CSS Masking Details

Two vendor-prefixed properties are set to ensure cross-browser support:

mask-image: conic-gradient(...);
-webkit-mask-image: conic-gradient(...);

The overlay uses pointer-events: none and user-select: none so that interaction passes through to the button underneath (which is disabled and transparent).

An rgba(22, 22, 24, 0.82) semi-transparent background on the overlay produces the greyed-out appearance. The mask controls where this background is visible.


Usage on the Home Page

The component is added to Pages/Pages/Home/HomePage.razor inside the first PaperComponent:

<CooldownButtonComponent CooldownSeconds="12"
                         Size="120"
                         OnClick="OnCooldownClick">
    Click Me
</CooldownButtonComponent>

The OnCooldownClick handler in the page's @code block currently returns Task.CompletedTask (a no-op). This is the extension point where real work (e.g. triggering a game action, calling an API, showing a toast) would go.


Adapting the Component

  • Change cooldown duration set CooldownSeconds to any positive integer.
  • Change button size set Size to any pixel dimension (button remains square).
  • Custom content pass any Blazor markup as ChildContent (text, icons, spinners).
  • Handle the click attach a handler to OnClick that returns Task or void.

The --cooldown-size CSS custom property is set inline on the wrapper so that the label, overlay, and button all scale together.