diff --git a/src/lib/viewers/controls/_styles.scss b/src/lib/viewers/controls/_styles.scss
new file mode 100644
index 000000000..e3655bd19
--- /dev/null
+++ b/src/lib/viewers/controls/_styles.scss
@@ -0,0 +1,41 @@
+@import '~box-ui-elements/es/styles/variables';
+
+@mixin bp-ControlButton($height: 48px, $width: 48px) {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: $width;
+ height: $height;
+ color: $white;
+ background: transparent;
+ border: 1px solid transparent;
+ outline: 0;
+ cursor: pointer;
+ opacity: .7;
+ transition: opacity 150ms;
+ user-select: none;
+ touch-action: manipulation;
+ zoom: 1;
+
+ &:focus,
+ &:hover {
+ opacity: 1;
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px fade-out($white, .5), 0 1px 2px fade-out($black, .9);
+ }
+
+ &:disabled {
+ cursor: default;
+ opacity: .2;
+ pointer-events: none;
+ }
+}
+
+@mixin bp-ControlGroup {
+ display: flex;
+ align-items: center;
+ margin-right: 4px;
+ margin-left: 4px;
+}
diff --git a/src/lib/viewers/controls/fullscreen/FullscreenToggle.scss b/src/lib/viewers/controls/fullscreen/FullscreenToggle.scss
new file mode 100644
index 000000000..8e4ac071b
--- /dev/null
+++ b/src/lib/viewers/controls/fullscreen/FullscreenToggle.scss
@@ -0,0 +1,5 @@
+@import 'src/lib/viewers/controls/styles';
+
+.bp-FullscreenToggle {
+ @include bp-ControlButton;
+}
diff --git a/src/lib/viewers/controls/fullscreen/FullscreenToggle.tsx b/src/lib/viewers/controls/fullscreen/FullscreenToggle.tsx
new file mode 100644
index 000000000..b6109093c
--- /dev/null
+++ b/src/lib/viewers/controls/fullscreen/FullscreenToggle.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import fullscreen from '../../../Fullscreen';
+import IconFullscreenIn24 from '../icons/IconFullscreenIn24';
+import IconFullscreenOut24 from '../icons/IconFullscreenOut24';
+import './FullscreenToggle.scss';
+
+export type Props = {
+ onFullscreenToggle: (isFullscreen: boolean) => void;
+};
+
+export default function FullscreenToggle({ onFullscreenToggle }: Props): JSX.Element {
+ const [isFullscreen, setFullscreen] = React.useState(false);
+ const Icon = isFullscreen ? IconFullscreenOut24 : IconFullscreenIn24;
+ const title = isFullscreen ? __('exit_fullscreen') : __('enter_fullscreen');
+
+ const handleClick = (): void => {
+ onFullscreenToggle(!isFullscreen);
+ };
+
+ React.useEffect(() => {
+ const handleFullscreenEnter = (): void => setFullscreen(true);
+ const handleFullscreenExit = (): void => setFullscreen(false);
+
+ fullscreen.addListener('enter', handleFullscreenEnter);
+ fullscreen.addListener('exit', handleFullscreenExit);
+
+ return (): void => {
+ fullscreen.removeListener('enter', handleFullscreenEnter);
+ fullscreen.removeListener('exit', handleFullscreenExit);
+ };
+ }, []);
+
+ return (
+
+ );
+}
diff --git a/src/lib/viewers/controls/fullscreen/__tests__/FullscreenToggle-test.tsx b/src/lib/viewers/controls/fullscreen/__tests__/FullscreenToggle-test.tsx
new file mode 100644
index 000000000..4b79f4942
--- /dev/null
+++ b/src/lib/viewers/controls/fullscreen/__tests__/FullscreenToggle-test.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import fullscreen from '../../../../Fullscreen';
+import FullscreenToggle from '../FullscreenToggle';
+import IconFullscreenIn24 from '../../icons/IconFullscreenIn24';
+import IconFullscreenOut24 from '../../icons/IconFullscreenOut24';
+
+describe('FullscreenToggle', () => {
+ const getWrapper = (props = {}): ShallowWrapper =>
+ shallow();
+
+ beforeEach(() => {
+ jest.spyOn(React, 'useEffect').mockImplementation(fn => fn());
+ });
+
+ describe('event handlers', () => {
+ test('should respond to fullscreen events', () => {
+ const wrapper = getWrapper();
+
+ fullscreen.enter();
+ expect(wrapper.exists(IconFullscreenOut24)).toBe(true);
+ expect(wrapper.prop('title')).toBe(__('exit_fullscreen'));
+
+ fullscreen.exit();
+ expect(wrapper.exists(IconFullscreenIn24)).toBe(true);
+ expect(wrapper.prop('title')).toBe(__('enter_fullscreen'));
+ });
+
+ test('should invoke onFullscreenToggle prop on click', () => {
+ const onToggle = jest.fn();
+ const wrapper = getWrapper({ onFullscreenToggle: onToggle });
+
+ wrapper.simulate('click');
+ expect(onToggle).toBeCalledWith(true);
+ });
+ });
+
+ describe('render', () => {
+ test('should return a valid wrapper', () => {
+ const wrapper = getWrapper();
+
+ expect(wrapper.hasClass('bp-FullscreenToggle')).toBe(true);
+ });
+ });
+});
diff --git a/src/lib/viewers/controls/fullscreen/index.ts b/src/lib/viewers/controls/fullscreen/index.ts
new file mode 100644
index 000000000..0fb941d6b
--- /dev/null
+++ b/src/lib/viewers/controls/fullscreen/index.ts
@@ -0,0 +1,2 @@
+export * from './FullscreenToggle';
+export { default } from './FullscreenToggle';
diff --git a/src/lib/viewers/controls/icons/IconZoomIn10.tsx b/src/lib/viewers/controls/icons/IconZoomIn10.tsx
index 11e87f92a..e27e0657f 100644
--- a/src/lib/viewers/controls/icons/IconZoomIn10.tsx
+++ b/src/lib/viewers/controls/icons/IconZoomIn10.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
-function IconZoomIn(props: React.SVGProps): JSX.Element {
+function IconZoomIn10(props: React.SVGProps): JSX.Element {
return (