Skip to content

Pending Review

Pending Review from User Experience (UX)

Heading Component

Overview

The PWC Heading is a typography hierarchy component in the Progress Web Components design system. It provides consistent heading structure with four size variations and proper semantic formatting for content organization across React and Angular applications.

Core Capabilities

  • Size Hierarchy - XXL, XL, L, and M sizes following design system typography scale
  • Color Variants - Base and disabled color options for different content contexts
  • Text Overflow Control - NoWrap option with ellipsis for constrained layouts
  • Source Sans Pro Typography - Consistent font family with proper line height ratios
  • Content Flexibility - Content property with slot support for complex markup
  • Semantic Structure - Proper heading markup for accessibility and SEO optimization

When to use Heading:

  • Create page titles, section headings, and content hierarchy structure
  • Establish visual importance and information architecture in interfaces
  • Display prominent text that introduces content sections and page areas
  • Provide semantic headings for screen readers and search engine optimization

When not to use Heading:

  • Body text or paragraph content (use Body component for regular text)
  • Interactive elements requiring click handlers (use Button or Link components)
  • Small labels or metadata that don't represent content hierarchy

Basic Implementation

import React, { useState, useCallback, useMemo } from 'react';
import { PwcHeading, PwcFlex, PwcButton, PwcBody } from '@progress-i360/pwc-react';

function PageTitle() {
  const [currentSection, setCurrentSection] = useState('dashboard');
  const [isCompactMode, setIsCompactMode] = useState(false);

  // React 18: Manual memoization for heading configuration
  const headingConfig = useMemo(() => {
    const configs = {
      dashboard: { content: 'Analytics Dashboard', size: 'xxl' },
      reports: { content: 'Quarterly Reports', size: 'xl' },
      settings: { content: 'System Configuration', size: 'l' },
      profile: { content: 'User Profile Settings', size: 'm' }
    };
    return configs[currentSection] || configs.dashboard;
  }, [currentSection]);

  // React 18: useCallback for performance optimization
  const cycleSections = useCallback(() => {
    const sections = ['dashboard', 'reports', 'settings', 'profile'];
    const currentIndex = sections.indexOf(currentSection);
    setCurrentSection(sections[(currentIndex + 1) % sections.length]);
  }, [currentSection]);

  const toggleCompactMode = useCallback(() => {
    setIsCompactMode(prev => !prev);
  }, []);

  // React 18: Computed values using useMemo
  const displaySize = useMemo(() => {
    if (isCompactMode) {
      const sizeMap = { xxl: 'xl', xl: 'l', l: 'm', m: 'm' };
      return sizeMap[headingConfig.size];
    }
    return headingConfig.size;
  }, [headingConfig.size, isCompactMode]);

  return (
      <PwcFlex direction="column" gap="m" alignItems="center">
        <PwcHeading 
          content={headingConfig.content}
          size={displaySize}
          noWrap={isCompactMode}
        />

        <PwcFlex gap="s">
          <PwcButton
            label="Next Section"
            variant="primary"
            onPwcClick={cycleSections}
          />
          <PwcButton
            label={`${isCompactMode ? 'Expand' : 'Compact'} Mode`}
            variant="outline"
            onPwcClick={toggleCompactMode}
          />
        </PwcFlex>

        <PwcBody 
          content={`Current: ${currentSection} (${displaySize} size)`}
          size="xs" 
          color="subtle"
        />
      </PwcFlex>
  );
}
import { Suspense, use, useCallback, useDeferredValue, useOptimistic, useState } from 'react';
import '@progress-i360/progress-web-components/heading';
import '@progress-i360/progress-web-components/body';
import '@progress-i360/progress-web-components/flex';
import '@progress-i360/progress-web-components/button';

// React 19+: Async resource for dynamic content
const fetchPageContext = async (pageId) => {
  await new Promise(resolve => setTimeout(resolve, 800));
  const contexts = {
    home: { title: 'Welcome to Progress Platform', subtitle: 'Your unified development environment' },
    projects: { title: 'Active Projects Overview', subtitle: 'Managing 12 development initiatives' },
    analytics: { title: 'Performance Analytics Hub', subtitle: 'Real-time insights and metrics' },
    team: { title: 'Team Collaboration Center', subtitle: 'Connect with 45+ team members' }
  };
  return contexts[pageId] || contexts.home;
};

type PageState = {
  id: 'home' | 'projects' | 'analytics' | 'team';
  active: boolean;
  loading: boolean;
};

function DynamicPageHeader() {
  const [pageStates, setPageStates] = useState<PageState[]>([
    { id: 'home', active: true, loading: false },
    { id: 'projects', active: false, loading: false },
    { id: 'analytics', active: false, loading: false },
    { id: 'team', active: false, loading: false }
  ]);

  // React 19+: Defer expensive page state calculations
  const deferredPageStates = useDeferredValue(pageStates);

  // React 19+: Optimistic page switching for instant feedback
  const [optimisticStates, setOptimisticState] = useOptimistic(
    deferredPageStates,
    (state, update) => state.map(page =>
      page.id === update.id ? { ...page, ...update } : { ...page, active: false }
    )
  );

  const activePage = optimisticStates.find(page => page.active) || optimisticStates[0];

  // React 19+: Use hook for async data fetching
  const pageContext = use(fetchPageContext(activePage.id));

  const switchPage = useCallback(
    async (targetPageId: PageState['id']) => {
      // React 19+: Optimistic update for instant UI feedback
      setOptimisticState({ id: targetPageId, active: true, loading: true });

      // Simulate page context loading
      await new Promise(resolve => setTimeout(resolve, 300));

      setPageStates(prev =>
        prev.map(page => ({
          ...page,
          active: page.id === targetPageId,
          loading: false
        }))
      );
    },
    [setOptimisticState, setPageStates]
  );

  const refreshContext = useCallback(() => {
    setPageStates(prev =>
      prev.map(page => (page.active ? { ...page, loading: true } : page))
    );

    // Force refresh by re-triggering context fetch
    setTimeout(() => {
      setPageStates(prev =>
        prev.map(page => (page.active ? { ...page, loading: false } : page))
      );
    }, 1000);
  }, [setPageStates]);

  return (
    <pwc-flex direction="column" gap="l" align-items="center">
      <Suspense fallback={<pwc-heading content="Loading..." size="xl" color="disabled"></pwc-heading>}>
        <pwc-flex direction="column" gap="s" align-items="center">
          <pwc-heading
            content={pageContext.title}
            size="xxl"
          ></pwc-heading>
          <pwc-body
            content={pageContext.subtitle}
            size="s"
            color="subtle"
          ></pwc-body>
        </pwc-flex>
      </Suspense>

      <pwc-flex gap="xs" wrap="wrap" justify-content="center">
        {optimisticStates.map(page => (
          <pwc-button
            key={page.id}
            label={page.id.charAt(0).toUpperCase() + page.id.slice(1)}
            variant={page.active ? 'primary' : 'outline'}
            onPwcClick={() => switchPage(page.id)}
            disabled={page.loading}
          ></pwc-button>
        ))}
      </pwc-flex>

      <pwc-button
        label="Refresh Context"
        variant="outline"
        onPwcClick={refreshContext}
      ></pwc-button>

      <pwc-body
        content={`Active: ${activePage.id} page ${activePage.loading ? '(loading...)' : ''}`}
        size="xs"
        color="subtler"
      ></pwc-body>
    </pwc-flex>
  );
}
import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import "@progress-i360/progress-web-components/heading";
import "@progress-i360/progress-web-components/flex";
import "@progress-i360/progress-web-components/button";
import "@progress-i360/progress-web-components/body";

@Component({
  selector: 'heading-demo',
  template: `
    <pwc-flex direction="column" gap="m" padding="m">
      <pwc-heading [content]="headingText" [size]="headingSize"></pwc-heading>
      <pwc-flex gap="s" wrap="wrap">
        <pwc-button label="XXL Size" variant="outline" (pwc-click)="setSize('xxl')"></pwc-button>
        <pwc-button label="XL Size" variant="outline" (pwc-click)="setSize('xl')"></pwc-button>
        <pwc-button label="L Size" variant="outline" (pwc-click)="setSize('l')"></pwc-button>
        <pwc-button label="M Size" variant="outline" (pwc-click)="setSize('m')"></pwc-button>
      </pwc-flex>
      <pwc-button label="Update Heading" variant="primary" (pwc-click)="updateHeading()"></pwc-button>
      <pwc-body [content]="'Current Size: ' + headingSize" color="subtle" size="xs"></pwc-body>
    </pwc-flex>
  `,
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class HeadingDemo {
  headingText = 'Dynamic Heading Example';
  headingSize = 'xl';
  headingCount = 0;

  setSize(size: string) {
    this.headingSize = size;
  }

  updateHeading() {
    this.headingCount++;
    this.headingText = `Updated Heading #${this.headingCount}`;
  }
}
import '@progress-i360/progress-web-components/heading';

const mainHeading = document.createElement('pwc-heading');
mainHeading.setAttribute('content', 'Dashboard');
mainHeading.setAttribute('size', 'xxl');

const sectionHeading = document.createElement('pwc-heading');
sectionHeading.setAttribute('content', 'Recent Activity');
sectionHeading.setAttribute('size', 'l');
sectionHeading.setAttribute('color', 'disabled');

document.querySelector('#page-header')?.appendChild(mainHeading);
document.querySelector('#section-header')?.appendChild(sectionHeading);

Usage Patterns

Heading components adapt to different content hierarchy scenarios:

  • Size Hierarchy - XXL for main titles, XL for major sections, L for subsections, M for minor headings
  • Color Variations - Base color for standard headings, disabled color for secondary or inactive headings
  • Text Overflow - NoWrap option with ellipsis for constrained layout areas
  • Content Flexibility - Content property for simple text, slot support for complex markup

Best Practices

Content Strategy Guidelines

  • Clear Hierarchy - Use size progression (XXL → XL → L → M) to establish content importance
  • Meaningful Titles - Write descriptive headings that accurately represent section content
  • Consistent Terminology - Apply uniform language patterns across similar content areas
  • Scannable Structure - Create heading hierarchy that supports quick content navigation

Performance Optimization

  • Efficient Rendering - Leverage component reusability for consistent heading performance
  • Font Loading - Ensure Source Sans Pro font loads properly for typography consistency
  • Memory Management - Avoid unnecessary re-renders for static heading content
  • Layout Optimization - Use noWrap option judiciously to prevent layout performance issues

Integration Architecture

  • Typography System - Integrate seamlessly with other PWC typography components
  • Design Token Usage - Leverage design system tokens for consistent sizing and colors
  • Semantic Structure - Maintain proper heading hierarchy for accessibility and SEO
  • Responsive Behavior - Ensure headings scale appropriately across screen sizes

Common Use Cases

Data Table Headers

  • Table Section Titles - Use L or M size headings for table section labels and categories
  • Column Group Headers - Apply M size headings for grouped column headers and classifications
  • Data Summary Headers - Implement XL or L headings for data summary sections and analytics

Search Result Sections

  • Search Category Headers - Use XL headings for major search result categories and filters
  • Result Group Titles - Apply L headings for search result groupings and classifications
  • Filter Section Headers - Implement M headings for search filter categories and options

Dashboard Widget Headers

  • Widget Main Titles - Use L or XL headings for primary dashboard widget titles
  • Section Labels - Apply M headings for dashboard section labels and widget groups
  • Chart Titles - Implement L headings for chart and visualization titles within widgets

Troubleshooting

Common Issues

Size Not Applying

Problem: Heading size doesn't change or displays incorrectly

Solution:

  • Verify size property matches available options (xxl, xl, l, m) and CSS tokens are loaded

Color Not Displaying

Problem: Heading color doesn't apply or shows incorrect color

Solution:

  • Check color property matches available options (base, disabled) and theme is configured

Text Overflow Issues

Problem: Long headings don't handle overflow properly

Solution:

  • Use noWrap property for ellipsis behavior or adjust container width constraints

Content Not Rendering

Problem: Heading content doesn't display or shows empty

Solution:

  • Verify content property is set correctly or use slot content for complex markup

Implementation Support

For detailed implementation guidance:

  • Typography Integration - Works seamlessly with PWC Body and other text components
  • Design System Tokens - Uses standardized sizing, color, and font tokens
  • Accessibility Support - Maintains proper semantic structure for screen readers and SEO

Resources

Storybook Documentation

For comprehensive API documentation, interactive examples, and testing tools: 📖 View Complete API Documentation in Storybook →


This guide provides high-level implementation guidance. For detailed API specifications and interactive examples, visit our Storybook documentation.