[Style] Compact Modern Nodes (#6687)

## Summary

Simple and clean is the way that we're making the nodes tonight.

## Changes

- **What**: Smaller minimum widths for nodes and labels
- **What**: Smaller font for the labels
- **What**: Removed outlines for widgets
- **What**: Fixes a text/background issue with buttons on widgets
- **What**: Smaller header
- **What**: Less padding within the node itself

## Review Focus

Check out the new styles and how they align with the Designs.

## Screenshots

| Before | After |
| --- | --- |
| <img width="542" height="486" alt="image"
src="https://github.com/user-attachments/assets/41fe9801-7a43-49ac-87fc-36d3b2ee82fb"
/> | <img width="411" height="388" alt="image"
src="https://github.com/user-attachments/assets/a7c21120-bf67-4039-86b3-c348bcc4341b"
/> |

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6687-Style-Compact-Modern-Nodes-2aa6d73d365081c48db3c5491c556dc9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Alexander Brown
2025-11-13 16:32:38 -08:00
committed by GitHub
parent f0f554392d
commit adfd2e514e
36 changed files with 64 additions and 94 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -10,11 +10,11 @@
/> />
<!-- Slot Name --> <!-- Slot Name -->
<div class="relative"> <div class="relative h-full flex items-center">
<span <span
v-if="!dotOnly" v-if="!dotOnly"
:class=" :class="
cn('whitespace-nowrap text-sm font-normal lod-toggle', labelClasses) cn('whitespace-nowrap text-xs font-normal lod-toggle', labelClasses)
" "
> >
{{ slotData.localized_name || slotData.name || `Input ${index}` }} {{ slotData.localized_name || slotData.name || `Input ${index}` }}

View File

@@ -83,7 +83,7 @@
/> />
<template v-if="!isCollapsed"> <template v-if="!isCollapsed">
<div class="relative mb-4"> <div class="relative mb-1">
<div :class="separatorClasses" /> <div :class="separatorClasses" />
<!-- Progress bar for executing state --> <!-- Progress bar for executing state -->
<div <div
@@ -101,7 +101,7 @@
<!-- Node Body - rendered based on LOD level and collapsed state --> <!-- Node Body - rendered based on LOD level and collapsed state -->
<div <div
class="flex min-h-min min-w-min flex-1 flex-col gap-4 pb-4" class="flex min-h-min min-w-min flex-1 flex-col gap-1 pb-2"
:data-testid="`node-body-${nodeData.id}`" :data-testid="`node-body-${nodeData.id}`"
> >
<!-- Slots only rendered at full detail --> <!-- Slots only rendered at full detail -->

View File

@@ -6,7 +6,7 @@
v-else v-else
:class=" :class="
cn( cn(
'lg-node-header p-4 rounded-t-2xl w-full min-w-50', 'lg-node-header py-2 pl-2 pr-3 text-sm rounded-t-2xl w-full min-w-50',
'text-node-component-header', 'text-node-component-header',
collapsed && 'rounded-2xl' collapsed && 'rounded-2xl'
) )

View File

@@ -5,7 +5,7 @@
<div v-else :class="cn('flex justify-between', unifiedWrapperClass)"> <div v-else :class="cn('flex justify-between', unifiedWrapperClass)">
<div <div
v-if="filteredInputs.length" v-if="filteredInputs.length"
:class="cn('flex flex-col gap-1', unifiedDotsClass)" :class="cn('flex flex-col', unifiedDotsClass)"
> >
<InputSlot <InputSlot
v-for="(input, index) in filteredInputs" v-for="(input, index) in filteredInputs"
@@ -19,7 +19,7 @@
<div <div
v-if="nodeData?.outputs?.length" v-if="nodeData?.outputs?.length"
:class="cn('ml-auto flex flex-col gap-1', unifiedDotsClass)" :class="cn('ml-auto flex flex-col', unifiedDotsClass)"
> >
<OutputSlot <OutputSlot
v-for="(output, index) in nodeData.outputs" v-for="(output, index) in nodeData.outputs"

View File

@@ -6,7 +6,7 @@
v-else v-else
:class=" :class="
cn( cn(
'lg-node-widgets flex flex-col has-[.widget-expands]:flex-1 gap-2 pr-3', 'lg-node-widgets flex flex-col has-[.widget-expands]:flex-1 gap-1 pr-3',
shouldHandleNodePointerEvents shouldHandleNodePointerEvents
? 'pointer-events-auto' ? 'pointer-events-auto'
: 'pointer-events-none' : 'pointer-events-none'

View File

@@ -1,11 +1,11 @@
<template> <template>
<div v-if="renderError" class="node-error p-1 text-xs text-red-500"></div> <div v-if="renderError" class="node-error p-1 text-xs text-red-500"></div>
<div v-else v-tooltip.right="tooltipConfig" :class="slotWrapperClass"> <div v-else v-tooltip.right="tooltipConfig" :class="slotWrapperClass">
<div class="relative"> <div class="relative h-full flex items-center">
<!-- Slot Name --> <!-- Slot Name -->
<span <span
v-if="!dotOnly" v-if="!dotOnly"
class="lod-toggle text-sm font-normal whitespace-nowrap text-node-component-slot-text" class="lod-toggle text-xs font-normal whitespace-nowrap text-node-component-slot-text"
> >
{{ slotData.localized_name || slotData.name || `Output ${index}` }} {{ slotData.localized_name || slotData.name || `Output ${index}` }}
</span> </span>

View File

@@ -10,7 +10,6 @@ import {
filterWidgetProps filterWidgetProps
} from '@/utils/widgetPropFilter' } from '@/utils/widgetPropFilter'
import { useNumberWidgetButtonPt } from '../composables/useNumberWidgetButtonPt'
import { WidgetInputBaseClass } from './layout' import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue' import WidgetLayoutField from './layout/WidgetLayoutField.vue'
@@ -79,57 +78,50 @@ const buttonTooltip = computed(() => {
} }
return null return null
}) })
const inputNumberPt = useNumberWidgetButtonPt({
roundedLeft: true,
roundedRight: true
})
</script> </script>
<template> <template>
<WidgetLayoutField :widget> <WidgetLayoutField :widget>
<div v-tooltip="buttonTooltip"> <InputNumber
<InputNumber v-model="localValue"
v-model="localValue" v-tooltip="buttonTooltip"
v-bind="filteredProps" v-bind="filteredProps"
button-layout="horizontal" fluid
size="small" button-layout="horizontal"
:step="stepValue" size="small"
:use-grouping="useGrouping" variant="outlined"
:class="cn(WidgetInputBaseClass, 'w-full text-xs')" :step="stepValue"
:aria-label="widget.name" :use-grouping="useGrouping"
:show-buttons="!buttonsDisabled" :class="cn(WidgetInputBaseClass, 'grow text-xs')"
:pt="inputNumberPt" :aria-label="widget.name"
@update:model-value="onChange" :show-buttons="!buttonsDisabled"
> :pt="{
<template #incrementicon> root: {
<span class: '[&>input]:bg-transparent [&>input]:border-0'
class="pi pi-plus text-sm text-component-node-foreground-secondary" },
/> decrementButton: {
</template> class: 'w-8 border-0'
<template #decrementicon> },
<span incrementButton: {
class="pi pi-minus text-sm text-component-node-foreground-secondary" class: 'w-8 border-0'
/> }
</template> }"
</InputNumber> @update:model-value="onChange"
</div> >
<template #incrementicon>
<span class="pi pi-plus text-sm" />
</template>
<template #decrementicon>
<span class="pi pi-minus text-sm" />
</template>
</InputNumber>
</WidgetLayoutField> </WidgetLayoutField>
</template> </template>
<style scoped> <style scoped>
:deep(.p-inputnumber-input) { :deep(.p-inputnumber-input) {
background-color: transparent;
border: 1px solid var(--component-node-border);
border-top: transparent;
border-bottom: transparent;
height: 1.625rem; height: 1.625rem;
margin: 1px 0; margin: 1px 0;
box-shadow: none; box-shadow: none;
} }
:deep(.p-inputnumber-button.p-disabled .pi),
:deep(.p-inputnumber-button.p-disabled .p-icon) {
color: var(--color-node-icon-disabled) !important;
}
</style> </style>

View File

@@ -1,10 +1,6 @@
<template> <template>
<WidgetLayoutField :widget="widget"> <WidgetLayoutField :widget="widget">
<div <div :class="cn(WidgetInputBaseClass, 'flex items-center gap-2 pl-3 pr-2')">
:class="
cn(WidgetInputBaseClass, 'flex items-center gap-2 w-full pl-4 pr-2')
"
>
<Slider <Slider
:model-value="[localValue]" :model-value="[localValue]"
v-bind="filteredProps" v-bind="filteredProps"
@@ -24,7 +20,6 @@
size="small" size="small"
pt:pc-input-text:root="min-w-full bg-transparent border-none text-center" pt:pc-input-text:root="min-w-full bg-transparent border-none text-center"
class="w-16" class="w-16"
:show-buttons="!buttonsDisabled"
:pt="sliderNumberPt" :pt="sliderNumberPt"
@update:model-value="handleNumberInputUpdate" @update:model-value="handleNumberInputUpdate"
/> />
@@ -107,14 +102,6 @@ const stepValue = computed(() => {
return 1 / Math.pow(10, precision.value) return 1 / Math.pow(10, precision.value)
}) })
const buttonsDisabled = computed(() => {
const currentValue = localValue.value ?? 0
return (
!Number.isFinite(currentValue) ||
Math.abs(currentValue) > Number.MAX_SAFE_INTEGER
)
})
const sliderNumberPt = useNumberWidgetButtonPt({ const sliderNumberPt = useNumberWidgetButtonPt({
roundedLeft: true, roundedLeft: true,
roundedRight: true roundedRight: true

View File

@@ -10,7 +10,7 @@
size="small" size="small"
:pt="{ :pt="{
option: 'text-xs', option: 'text-xs',
dropdownIcon: 'text-component-node-foreground-secondary' dropdown: 'w-8'
}" }"
data-capture-wheel="true" data-capture-wheel="true"
@update:model-value="onChange" @update:model-value="onChange"

View File

@@ -1,8 +1,9 @@
<template> <template>
<WidgetLayoutField :widget="widget"> <WidgetLayoutField :widget>
<ToggleSwitch <ToggleSwitch
v-model="localValue" v-model="localValue"
v-bind="filteredProps" v-bind="filteredProps"
class="ml-auto block"
:aria-label="widget.name" :aria-label="widget.name"
@update:model-value="onChange" @update:model-value="onChange"
/> />
@@ -42,13 +43,3 @@ const filteredProps = computed(() =>
filterWidgetProps(props.widget.options, STANDARD_EXCLUDED_PROPS) filterWidgetProps(props.widget.options, STANDARD_EXCLUDED_PROPS)
) )
</script> </script>
<style scoped>
:deep(.p-toggleswitch .p-toggleswitch-slider) {
border: 1px solid transparent;
}
:deep(.p-toggleswitch:hover .p-toggleswitch-slider) {
border-color: currentColor;
}
</style>

View File

@@ -11,19 +11,19 @@ defineProps<{
</script> </script>
<template> <template>
<div <div class="flex h-[30px] min-w-78 items-center justify-between gap-1">
class="flex h-[30px] min-w-105 items-center justify-between gap-2 overscroll-contain contain-size" <div
> class="relative flex h-full basis-content min-w-20 flex-1 items-center"
<div class="relative flex h-6 min-w-28 shrink-1 items-center"> >
<p <p
v-if="widget.name" v-if="widget.name"
class="lod-toggle flex-1 truncate text-sm font-normal text-node-component-slot-text" class="lod-toggle flex-1 truncate text-xs font-normal text-node-component-slot-text"
> >
{{ widget.label || widget.name }} {{ widget.label || widget.name }}
</p> </p>
<LODFallback /> <LODFallback />
</div> </div>
<div class="relative min-w-75 grow-1"> <div class="relative min-w-56 basis-full grow">
<div <div
class="lod-toggle cursor-default" class="lod-toggle cursor-default"
@pointerdown.stop="noop" @pointerdown.stop="noop"

View File

@@ -6,9 +6,6 @@ export const WidgetInputBaseClass = cn([
'not-disabled:text-component-node-foreground', 'not-disabled:text-component-node-foreground',
// Outline // Outline
'border-none', 'border-none',
'outline outline-offset-[-1px] outline-component-node-border',
// Rounded // Rounded
'rounded-lg', 'rounded-lg'
// Hover
'hover:bg-component-node-widget-background-hovered'
]) ])

View File

@@ -1,7 +1,10 @@
const sharedButtonClasses = import { cn } from '@comfyorg/tailwind-utils'
'!inline-flex !items-center !justify-center !border-0 bg-transparent text-inherit transition-colors duration-150 ease-in-out ' +
'hover:bg-node-component-surface-hovered active:bg-node-component-surface-selected' + const sharedButtonClasses = cn(
'inline-flex items-center justify-center border-0 bg-transparent text-inherit transition-colors duration-150 ease-in-out ',
'hover:bg-node-component-surface-hovered active:bg-node-component-surface-selected',
'disabled:bg-node-component-disabled disabled:text-node-icon-disabled disabled:cursor-not-allowed' 'disabled:bg-node-component-disabled disabled:text-node-icon-disabled disabled:cursor-not-allowed'
)
export function useNumberWidgetButtonPt(options?: { export function useNumberWidgetButtonPt(options?: {
roundedLeft?: boolean roundedLeft?: boolean
@@ -9,15 +12,15 @@ export function useNumberWidgetButtonPt(options?: {
}) { }) {
const { roundedLeft = false, roundedRight = false } = options ?? {} const { roundedLeft = false, roundedRight = false } = options ?? {}
const increment = `${sharedButtonClasses}${roundedRight ? ' !rounded-r-lg' : ''}` const increment = cn(sharedButtonClasses, roundedRight && 'rounded-r-lg')
const decrement = `${sharedButtonClasses}${roundedLeft ? ' !rounded-l-lg' : ''}` const decrement = cn(sharedButtonClasses, roundedLeft && 'rounded-l-lg')
return { return {
incrementButton: { incrementButton: {
class: increment.trim() class: increment
}, },
decrementButton: { decrementButton: {
class: decrement.trim() class: decrement
} }
} }
} }