Skip to content

Conversation

@ggdouglas
Copy link
Contributor

@ggdouglas ggdouglas commented Oct 16, 2025

Proposed Changes

Original draft proposal: #6980

This PR introduces new <Box>, <Flex>, and <Slot> components to @blueprintjs/labs. These components provide flexible layout utilities for margin, padding, gap, alignment, and positioning.

Components:

  • <Box> is the foundational layout primitive with utility props that generate CSS classes
  • <Flex> is a specialized Box component pre-configured with display="flex"
  • <Slot> is used by Box for polymorphic composition via asChild

Screenshot 2025-11-05 at 10 55 17@2x

Examples

Arranging multiple Buttons horizontally:

<Box display="flex" gap={2}>
  <Button intent="primary">Button</Button>
  <Button intent="success">Button</Button>
  <Button intent="warning">Button</Button>
  <Button intent="danger">Button</Button>
</Box>

CleanShot 2024-09-17 at 14 42 02@2x

Or stacked vertically:

<Box display="flex" flexDirection="column" alignItems="center" gap={2}>
  <Button intent="primary">Button</Button>
  <Button intent="success">Button</Button>
  <Button intent="warning">Button</Button>
  <Button intent="danger">Button</Button>
</Box>

CleanShot 2024-09-17 at 14 44 20@2x

Creating more molecular components (such as EntityTitle):

<Box display="flex" gap={2}>
    <Icon className={Classes.TEXT_MUTED} icon="shop" />
    Buy groceries on my way home
    <Box asChild={true} marginXStart="auto">
        <Tag intent="danger" minimal={true}>
            Due today
        </Tag>
    </Box>
</Box>

CleanShot 2024-09-17 at 14 56 02@2x

@ggdouglas ggdouglas marked this pull request as ready for review October 16, 2025 18:26
@ggdouglas ggdouglas force-pushed the gdouglas/box-and-flex-components branch from 95b2da7 to 88435ea Compare October 16, 2025 18:38
@svc-palantir-github
Copy link

Add Box demo and update demo-app components

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Copy link
Contributor

@ericjeney ericjeney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 🎉 🎉

I'll let @mm-wang do the full review, but left a few preliminary comments on the things that jumped out 🙂


.#{$ns}-box {
@each $i, $value in $sizes {
&.gap-#{$i} {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add #{$ns}- on all of the classes in this file to avoid conflicting with classes that may be defined in consuming apps?

I think that nesting it all under .#{$ns}-box is sufficient to avoid us polluting an application's CSS, but doesn't do anything to prevent the app's CSS from polluting Blueprint's components.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'll take the point about app CSS potentially polluting these utlity classes under consideration. 🤔

I'm hesitant to add the namespace to all utility classes mainly because of verbosity. Like, we'd end up with something like this:

<div class="bp6-box bp6-flex bp6-gap-2 bp6-padding-4 bp6-margin-2">

That would make DOM inspection pretty cluttered and harder to scan at a glance. On the flip side, the main downside of not adding the prefix is the risk of conflicts with other app-level utility frameworks that could potentially collide with Blueprint's utility classes.

My thinking is that many modern apps already use CSS Modules, CSS-in-JS, or scoped styling approaches, which naturally reduce global namespace pollution. Plus, the .bp6-box parent class already provides some scoping since you have to explicitly apply it to use these utilities. And honestly, users who want comprehensive utility classes are probably already reaching for Tailwind or similar frameworks anyway.

Also worth noting that users primarily interact with <Box> via React props (like <Box gap={2} />), so the actual class names are somewhat of an implementation detail.

Copy link
Member

@invliD invliD Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely keep the prefix here, mainly for the reason Eric mentioned, but also for consistency. All of our CSS classes have the prefix, and we shouldn't just change that here. I don't think the additional prefix causes any issues in practice, because like you mentioned people don't write them directly; they either use the Box component, or the Classes constants.

I don't think we should rely on consumers having certain build environments or using certain libraries that may make not having the prefix a non-issue. We can almost guarantee it by just having the namespace, at basically no cost to us.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefix added in 6beb43b

@svc-palantir-github
Copy link

Export `FlexProps`

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Copy link
Contributor

@mm-wang mm-wang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to test it out, but wanted to leave these comments here first. I like how this is shaping up though!
I'm curious your thoughts on a Grid layout component as well - I know it doesn't have many properties, but it makes sense to me to have an "alternative" to Flex that has its own set of typical properties.

/**
* Slot component.
*/
export const Slot = forwardRef<HTMLElement, React.HTMLAttributes<HTMLElement> & { children?: React.ReactNode }>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question/nit: As far as I understand, Slot is a placeholder for either Box or Flex type of layout component. I see we're not exposing it by exporting, but I would maybe make it a bit clearer in terms of naming - perhaps LayoutPlaceholder or something to make it clear it's not intended to be a component that will eventually be used. Definitely a nit though! I don't feel super strongly about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary reason for this being called Slot is that it mirrors/is inspired by the Radix primitive of the same name. I'm open to calling it something else, but I'm not sure if LayoutPlaceholder really conveys the meaning to me more vs. Slot.

} from "@blueprintjs/core";
import { Example, type ExampleProps } from "@blueprintjs/docs-theme";

const SPACING_VALUES: Array<BoxProps["padding"]> = [0, 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This seems to be used a lot in your examples, should it be an exported constant? I'm not sure - if we add to it/change it in the future, then we run into issues. Would love your thoughts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally ok with leaving a bit of duplication (esp. in examples/docs) for now until we find a reason for Blueprint consumers to ask for/need the exported constant. I can't think of a great reason for including it from the inception of these components, but I could be persuaded later / we could always add it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was convinced that we can add this now. Implementation: cf1d575

/**
* The range of values for size as a percentage.
*/
type SizeRange = 25 | 50 | 75 | 100;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: From the draft, I'd also wondered - why only quarters here? Why not thirds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linking to your original comment: #6980 (comment)

Not opposed to adding more options here, but I'm worried that this potentially doesn't scale well to individual classes. How many fractional values do we want to support?

Widths/heights seem like they're perhaps better suited to dynamic computation/inline styles. Something I've toyed around with, but is not formally implemented in this layout components proposal.

@svc-palantir-github
Copy link

Prefix utility classes with `bp6` namespace

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@svc-palantir-github
Copy link

Revert inclusion of `pt-spacing` CSS variable at root

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@ggdouglas ggdouglas force-pushed the gdouglas/box-and-flex-components branch from 28c66ce to 784a7cb Compare October 21, 2025 16:20
@svc-palantir-github
Copy link

Modify options for align/justify content for closer parity with CSS

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@svc-palantir-github
Copy link

Modify box tests to explicitly test for data attributes

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@svc-palantir-github
Copy link

Expose SpacingRange constant and type

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@svc-palantir-github
Copy link

Revert "Expose SpacingRange constant and type"

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@ggdouglas ggdouglas force-pushed the gdouglas/box-and-flex-components branch from 0a0e02e to b2ccddd Compare October 31, 2025 13:49
@svc-palantir-github
Copy link

Revert "Expose SpacingRange constant and type"

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@ggdouglas ggdouglas force-pushed the gdouglas/box-and-flex-components branch from b2ccddd to 78c1ed8 Compare November 5, 2025 02:19
@ggdouglas ggdouglas force-pushed the gdouglas/box-and-flex-components branch from 78c1ed8 to 01345dd Compare November 5, 2025 04:16
@svc-palantir-github
Copy link

Move components to labs

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@ggdouglas ggdouglas changed the title [core] Introduce <Box>and <Flex> components [labs] Introduce <Box>and <Flex> components Nov 5, 2025
"lint-fix": "es-lint --fix && sass-lint --fix",
"start": "pnpm dev",
"test": "vitest run",
"test:watch": "vitest watch",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this as a convenience helper when writing/running tests locally.


// declare let BLUEPRINT_NAMESPACE: string | undefined;
// declare let REACT_APP_BLUEPRINT_NAMESPACE: string | undefined;
const NS = Classes.getClassNamespace();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYSA @mm-wang, I ended up pulling the namespace from the core package for a few reasons:

  1. We need to pull in core anyways for the use of the $pt-spacing variable that the Box component uses
  2. We need to use the $ns namespace in the _box.scss also, and this was proving to be more complicated with us redeclaring a new namespace in labs
  3. The other packages (datetime, select, and table) all follow this same pattern, so there is precedent
  4. We end up with less duplication overall by reusing the namespace already declared in core

Copy link
Contributor Author

@ggdouglas ggdouglas Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed further and decided to combine both approaches. The namespace will now be .bp6-labs-*.

Implemented in e4aa04c


// Empty for no components right now
// Example: @use "./sampleComponent/sample-component";
/// <reference types="@testing-library/jest-dom/vitest" />
Copy link
Contributor Author

@ggdouglas ggdouglas Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff here is a bit confusing. A new file, packages/labs/src/vitest-env.d.ts, was added. This was done to pick up the types from @testing-library/jest-dom / fix compilation in the labs package.

@svc-palantir-github
Copy link

Add `-labs` to namespace prefix inside of labs components

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@svc-palantir-github
Copy link

Add back labs-scoped `DISPLAYNAME_PREFIX`

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Copy link
Contributor

@mm-wang mm-wang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🚀


import { DISPLAYNAME_PREFIX as CORE_DISPLAYNAME_PREFIX } from "@blueprintjs/core";

export const DISPLAYNAME_PREFIX = `${CORE_DISPLAYNAME_PREFIX}Labs`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm-wang
mm-wang previously approved these changes Nov 5, 2025
@policy-bot policy-bot bot dismissed mm-wang’s stale review November 5, 2025 22:07

Invalidated by push of 12c2cf2

@svc-palantir-github
Copy link

Address docs feedback

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@svc-palantir-github
Copy link

Add Flex props interface for posterity

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

@ggdouglas ggdouglas merged commit f88e977 into develop Nov 6, 2025
14 checks passed
@ggdouglas ggdouglas deleted the gdouglas/box-and-flex-components branch November 6, 2025 00:42
@ggdouglas ggdouglas mentioned this pull request Dec 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants