@codecademy/styleguide 78.5.6-alpha.ea1cbb.0 → 78.5.6-alpha.f24d55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -52,6 +52,9 @@ export const DocsContainer: React.FC<{
52
52
 
53
53
  const globalTheme =
54
54
  (context as any).store.userGlobals?.globals?.theme || 'core';
55
+ const globalLogicalProps = (context as any).store.userGlobals?.globals
56
+ ?.logicalProps;
57
+ const useLogicalProperties = globalLogicalProps !== 'false';
55
58
 
56
59
  const { currentTheme } = useMemo(() => {
57
60
  const findThemeStory: keyof typeof themeSpecificStories | undefined =
@@ -77,6 +80,7 @@ export const DocsContainer: React.FC<{
77
80
  cache={createEmotionCache({ speedy: false })}
78
81
  // This is typed to the CoreTheme in theme.d.ts
79
82
  theme={currentTheme as unknown as CoreTheme}
83
+ useLogicalProperties={useLogicalProperties}
80
84
  >
81
85
  <ColorMode mode="light">
82
86
  <HelmetProvider>
@@ -163,6 +163,19 @@ export const globalTypes = {
163
163
  showName: true,
164
164
  },
165
165
  },
166
+ logicalProps: {
167
+ name: 'LogicalProps',
168
+ description: 'Toggle between logical and physical CSS properties',
169
+ defaultValue: 'true',
170
+ toolbar: {
171
+ icon: 'transfer',
172
+ items: [
173
+ { value: 'true', title: 'Logical' },
174
+ { value: 'false', title: 'Physical' },
175
+ ],
176
+ showName: true,
177
+ },
178
+ },
166
179
  };
167
180
 
168
181
  export const decorators = [withEmotion];
@@ -34,12 +34,14 @@ type GlobalsContext = {
34
34
  globals: {
35
35
  colorMode: 'light' | 'dark';
36
36
  theme: keyof typeof themeMap;
37
+ logicalProps: 'true' | 'false';
37
38
  };
38
39
  };
39
40
 
40
41
  export const withEmotion = (Story: any, context: GlobalsContext) => {
41
42
  const colorMode = context.globals.colorMode ?? 'light';
42
43
  const selectedTheme = context.globals.theme;
44
+ const useLogicalProperties = context.globals.logicalProps !== 'false';
43
45
  const background = corePalette[themeBackground[colorMode]];
44
46
  const storyRef = useRef<HTMLDivElement>(null);
45
47
  const currentTheme = themeMap[selectedTheme];
@@ -57,6 +59,7 @@ export const withEmotion = (Story: any, context: GlobalsContext) => {
57
59
  <GamutProvider
58
60
  useCache={false}
59
61
  useGlobals={false}
62
+ useLogicalProperties={useLogicalProperties}
60
63
  theme={currentTheme as unknown as Theme}
61
64
  >
62
65
  <Background
@@ -72,7 +75,10 @@ export const withEmotion = (Story: any, context: GlobalsContext) => {
72
75
 
73
76
  // Wrap all stories in minimal provider
74
77
  return (
75
- <GamutProvider theme={currentTheme as unknown as Theme}>
78
+ <GamutProvider
79
+ useLogicalProperties={useLogicalProperties}
80
+ theme={currentTheme as unknown as Theme}
81
+ >
76
82
  <Background
77
83
  alwaysSetVariables
78
84
  bg={themeBackground[colorMode]}
package/CHANGELOG.md CHANGED
@@ -3,7 +3,7 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ### [78.5.6-alpha.ea1cbb.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@78.5.5...@codecademy/styleguide@78.5.6-alpha.ea1cbb.0) (2026-01-14)
6
+ ### [78.5.6-alpha.f24d55.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@78.5.5...@codecademy/styleguide@78.5.6-alpha.f24d55.0) (2026-01-20)
7
7
 
8
8
  **Note:** Version bump only for package @codecademy/styleguide
9
9
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@codecademy/styleguide",
3
3
  "description": "Styleguide & Component library for codecademy.com",
4
- "version": "78.5.6-alpha.ea1cbb.0",
4
+ "version": "78.5.6-alpha.f24d55.0",
5
5
  "author": "Codecademy Engineering",
6
6
  "license": "MIT",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
10
10
  "repository": "git@github.com:Codecademy/gamut.git",
11
- "gitHead": "c2969b8bc6d5586862a0c05b228db55a08c1c94a"
11
+ "gitHead": "1eb9fd1e7ca7666a9c3d55ce651b2048de3b4f58"
12
12
  }
@@ -1,8 +1,9 @@
1
- import { Meta } from '@storybook/blocks';
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
2
 
3
- import { AboutHeader, TokenTable } from '~styleguide/blocks';
3
+ import { AboutHeader, Callout, TokenTable } from '~styleguide/blocks';
4
4
 
5
5
  import { defaultColumns, getPropRows } from '../../shared/elements';
6
+ import * as SpaceStories from './Space.stories';
6
7
 
7
8
  export const parameters = {
8
9
  title: 'Space',
@@ -10,7 +11,7 @@ export const parameters = {
10
11
  status: 'updating',
11
12
  };
12
13
 
13
- <Meta title="Foundations/System/Props/Space" />
14
+ <Meta of={SpaceStories} title="Foundations/System/Props/Space" />
14
15
 
15
16
  <AboutHeader {...parameters} />
16
17
 
@@ -25,4 +26,16 @@ const SpaceExample = styled.div(system.space);
25
26
  <SpaceExample p={8} my={[16, 24, 32]} />;
26
27
  ```
27
28
 
29
+ <Callout
30
+ text={
31
+ <>
32
+ Props like <code>mx</code> and <code>my</code> support both physical and
33
+ logical CSS properties. Use the <strong>LogicalProps</strong> toolbar to
34
+ switch between modes.
35
+ </>
36
+ }
37
+ />
38
+
39
+ <Canvas of={SpaceStories.MarginExample} />
40
+
28
41
  <TokenTable rows={getPropRows('space')} columns={defaultColumns} />
@@ -0,0 +1,28 @@
1
+ import { Box } from '@codecademy/gamut';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+
4
+ const meta: Meta<typeof Box> = {
5
+ title: 'Foundations/System/Props/Space',
6
+ component: Box,
7
+ };
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof Box>;
11
+
12
+ export const MarginExample: Story = {
13
+ render: () => (
14
+ <Box
15
+ bg="primary"
16
+ color="white"
17
+ mb={64}
18
+ ml={16}
19
+ mr={32}
20
+ mt={4}
21
+ textAlign="center"
22
+ >
23
+ This box has <code>mt={4}</code>, <code>mr={32}</code>,{' '}
24
+ <code>mb={64}</code>, and <code>ml={16}</code>. Inspect the example to see
25
+ what CSS properties are rendered.
26
+ </Box>
27
+ ),
28
+ };
@@ -9,6 +9,7 @@ import {
9
9
  } from '@codecademy/gamut-styles';
10
10
  // eslint-disable-next-line gamut/import-paths
11
11
  import * as ALL_PROPS from '@codecademy/gamut-styles/src/variance/config';
12
+ import { useTheme } from '@emotion/react';
12
13
  import styled from '@emotion/styled';
13
14
  import kebabCase from 'lodash/kebabCase';
14
15
 
@@ -412,29 +413,61 @@ export const DarkModeTable = () => (
412
413
  );
413
414
  /* eslint-disable gamut/import-paths */
414
415
 
416
+ const PropertiesRenderer = ({
417
+ property,
418
+ properties,
419
+ resolveProperty,
420
+ }: {
421
+ property: string | { physical: string; logical: string };
422
+ properties?: string[] | { physical: string[]; logical: string[] };
423
+ resolveProperty?: (useLogicalProperties: boolean) => 'logical' | 'physical';
424
+ }) => {
425
+ const currentTheme = useTheme() as { useLogicalProperties?: boolean };
426
+ const useLogicalProperties = currentTheme?.useLogicalProperties ?? true;
427
+
428
+ const mode = resolveProperty
429
+ ? resolveProperty(useLogicalProperties)
430
+ : 'physical';
431
+
432
+ const resolvedProperty =
433
+ typeof property === 'string' ? property : property[mode];
434
+
435
+ let resolvedProperties: string[];
436
+ if (!properties) {
437
+ resolvedProperties = [resolvedProperty];
438
+ } else if (Array.isArray(properties)) {
439
+ resolvedProperties = properties;
440
+ } else {
441
+ resolvedProperties = properties[mode];
442
+ }
443
+
444
+ return (
445
+ <>
446
+ {resolvedProperties.map((prop) => (
447
+ <Anchor
448
+ href={`https://developer.mozilla.org/en-US/docs/Web/CSS/${kebabCase(
449
+ prop
450
+ )}`}
451
+ key={prop}
452
+ rel=""
453
+ target="_blank"
454
+ >
455
+ <AnchorCode>{kebabCase(prop)}</AnchorCode>
456
+ </Anchor>
457
+ ))}
458
+ </>
459
+ );
460
+ };
461
+
415
462
  const PROPERTIES_COLUMN = {
416
463
  key: 'properties',
417
464
  name: 'Properties',
418
465
  size: 'xl',
419
- render: ({
420
- property,
421
- properties = [property],
422
- }: {
423
- property: string;
424
- properties: string[];
425
- }) =>
426
- properties.map((property) => (
427
- <Anchor
428
- href={`https://developer.mozilla.org/en-US/docs/Web/CSS/${kebabCase(
429
- property
430
- )}`}
431
- key={property}
432
- rel=""
433
- target="_blank"
434
- >
435
- <AnchorCode>{kebabCase(property)}</AnchorCode>
436
- </Anchor>
437
- )),
466
+ render: (props: {
467
+ property: string | { physical: string; logical: string };
468
+ properties?: string[] | { physical: string[]; logical: string[] };
469
+ resolveProperty?: (useLogicalProperties: boolean) => 'logical' | 'physical';
470
+ }) => <PropertiesRenderer {...props} />,
438
471
  };
439
472
 
440
473
  const SCALE_COLUMN = {
@@ -450,7 +483,12 @@ const TRANSFORM_COLUMN = {
450
483
  key: 'transform',
451
484
  name: 'Transform',
452
485
  size: 'fill',
453
- render: ({ transform }: any) => transform && <Code>{transform?.name}</Code>,
486
+ render: ({ transform, resolveProperty }: any) => (
487
+ <>
488
+ {transform && <Code>{transform?.name}</Code>}
489
+ {resolveProperty && <Code>{resolveProperty?.name}</Code>}
490
+ </>
491
+ ),
454
492
  };
455
493
 
456
494
  export const defaultColumns = [
@@ -20,7 +20,6 @@ export const Bordered: Story = {
20
20
  border: 1,
21
21
  p: 8,
22
22
  children: 'I am a bordered box',
23
- ml: 24,
24
23
  },
25
24
  };
26
25