diff --git a/.changeset/fuzzy-tips-build.md b/.changeset/fuzzy-tips-build.md
new file mode 100644
index 00000000..7c1bb9e5
--- /dev/null
+++ b/.changeset/fuzzy-tips-build.md
@@ -0,0 +1,5 @@
+---
+'spectacle': minor
+---
+
+Add `FitText` typography component.
diff --git a/docs/api-reference.md b/docs/api-reference.md
index fd085794..6fe4b8d6 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -56,6 +56,7 @@ These tags are for displaying textual content.
| Tag Name | Theme Props | Additional Props | Default Props |
|---------------------|-------------------------------------------------------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`Text`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **color**: primary
**fontFamily**: text
**fontSize**: text
**textAlign**: left
**margin**: textMargin |
+| **`FitText`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **color**: primary
**fontFamily**: text
**fontSize**: text
**textAlign**: center
**margin**: textMargin |
| **`Heading`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **color**: secondary
**fontFamily**: header
**fontSize**: h1
**fontWeight**: bold
**textAlign**: center
**margin**: headerMargin |
| **`Link`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography)
| **href**: PropTypes.string | **color**: quaternary
**fontFamily**: text
**fontSize**: text
**textDecoration**: underline
**textAlign**: left
**margin**: textMargin |
| **`Quote`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography)
| — | **color**: primary
**fontFamily**: text
**fontSize**: text
**textAlign**: left
**borderLeft**: 1px solid secondary |
diff --git a/examples/js/index.js b/examples/js/index.js
index 2ae8f6de..b4e063b4 100644
--- a/examples/js/index.js
+++ b/examples/js/index.js
@@ -12,6 +12,7 @@ import {
Slide,
Deck,
Text,
+ FitText,
Grid,
Box,
Image,
@@ -138,6 +139,19 @@ const Presentation = () => (
+
+ This is a Heading
+
+ This is a FitText component
+
+
+ Shorter fit text
+
+ This is a Text. (Resize this window!)
+
These
diff --git a/examples/one-page/index.html b/examples/one-page/index.html
index 5cf3f921..b7adc7c6 100644
--- a/examples/one-page/index.html
+++ b/examples/one-page/index.html
@@ -60,6 +60,7 @@
Slide,
Deck,
Text,
+ FitText,
Grid,
Box,
Image,
@@ -170,6 +171,15 @@
${Appear}>
${OrderedList}>
${Slide}>
+ <${Slide}>
+ <${Heading}>This is a Heading${Heading}>
+ <${FitText}>This is a <${CodeSpan}>FitText${CodeSpan}> component${FitText}>
+ <${FitText} color="secondary" style=${{
+ textTransform: 'uppercase',
+ fontFamily: 'Comic Sans MS'
+ }}>Shorter fit text${FitText}>
+ <${Text}>This is a Text. (Resize this window!)${Text}>
+ ${Slide}>
<${Slide}>
<${FlexBox}>
<${Text}>These${Text}>
diff --git a/examples/typescript/index.tsx b/examples/typescript/index.tsx
index 88aa0155..15337ced 100644
--- a/examples/typescript/index.tsx
+++ b/examples/typescript/index.tsx
@@ -12,6 +12,7 @@ import {
Slide,
Deck,
Text,
+ FitText,
Grid,
Box,
Image,
@@ -137,6 +138,19 @@ const Presentation = () => (
+
+ This is a Heading
+
+ This is a FitText component
+
+
+ Shorter fit text
+
+ This is a Text. (Resize this window!)
+
These
diff --git a/packages/spectacle/src/components/typography.test.tsx b/packages/spectacle/src/components/typography.test.tsx
index 9ea28997..b8eb7338 100644
--- a/packages/spectacle/src/components/typography.test.tsx
+++ b/packages/spectacle/src/components/typography.test.tsx
@@ -10,7 +10,8 @@ import {
UnorderedList,
ListItem,
Link,
- CodeSpan
+ CodeSpan,
+ FitText
} from './typography';
import { render } from '@testing-library/react';
@@ -91,3 +92,32 @@ describe('', () => {
expect(container.querySelector('code')?.innerHTML).toBe('Code!');
});
});
+
+describe('', () => {
+ beforeEach(() => {
+ // Default mock implementation
+ jest.mock('use-resize-observer', () => {
+ return { width: 500, height: 100 };
+ });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render text content correctly', () => {
+ const { getByText } = mountWithTheme(Spectacle!);
+ expect(getByText('Spectacle!')).toBeInTheDocument();
+ });
+
+ it('should apply color and typography props correctly', () => {
+ const { getByText } = mountWithTheme(
+
+ Spectacle!
+
+ );
+ const textElement = getByText('Spectacle!');
+ expect(textElement).toHaveStyle({ color: defaultTheme.colors.secondary });
+ expect(textElement).toHaveStyle({ fontSize: 'h1' });
+ });
+});
diff --git a/packages/spectacle/src/components/typography.tsx b/packages/spectacle/src/components/typography.tsx
index 3e67c920..895acaa8 100644
--- a/packages/spectacle/src/components/typography.tsx
+++ b/packages/spectacle/src/components/typography.tsx
@@ -10,7 +10,15 @@ import {
SpaceProps,
BorderProps
} from 'styled-system';
-import { FC, PropsWithChildren } from 'react';
+import {
+ FC,
+ PropsWithChildren,
+ RefAttributes,
+ useRef,
+ useState,
+ HTMLAttributes
+} from 'react';
+import useResizeObserver from 'use-resize-observer';
const decoration = system({ textDecoration: true });
type DecorationProps = Pick;
@@ -113,6 +121,54 @@ ListItem.defaultProps = {
margin: 0
};
+const FitContainer = styled.div`
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+const ScalableText = styled(
+ Text as FC>
+)<{ scale: number }>`
+ transform-origin: center;
+ transform: scale(${(props) => props.scale});
+ white-space: nowrap;
+`;
+ScalableText.defaultProps = {
+ ...Text.defaultProps,
+ textAlign: 'center',
+ scale: 1
+};
+
+const FitText: FC<
+ PropsWithChildren>
+> = (props) => {
+ const containerRef = useRef(null);
+ const textRef = useRef(null);
+ const [scale, setScale] = useState(1);
+
+ useResizeObserver({
+ ref: containerRef,
+ onResize: () => {
+ if (!containerRef.current || !textRef.current) return;
+
+ const containerWidth = containerRef.current.offsetWidth;
+ const textWidth = textRef.current.offsetWidth;
+ if (textWidth === 0) return;
+
+ const newScale = Math.min(containerWidth / textWidth);
+ setScale(newScale);
+ }
+ });
+
+ return (
+
+
+
+ );
+};
+
export {
Text,
Heading,
@@ -121,5 +177,6 @@ export {
UnorderedList,
ListItem,
Link,
- CodeSpan
+ CodeSpan,
+ FitText
};
diff --git a/packages/spectacle/src/index.ts b/packages/spectacle/src/index.ts
index e6e75ff8..ee448226 100644
--- a/packages/spectacle/src/index.ts
+++ b/packages/spectacle/src/index.ts
@@ -10,7 +10,8 @@ export {
UnorderedList,
Text,
Link,
- CodeSpan
+ CodeSpan,
+ FitText
} from './components/typography';
export {
Table,