Appearance
Native Backgrounds And Gradients
Status
The first schema-validated implementation slice is in place for simple linear gradient fills on slide backgrounds and shape fills.
The configured ooxml and microsoft_learn MCP servers were consulted on 2026-06-07. The OOXML shape below is schema-grounded, but PowerPoint-authored comparison fixtures are still required before broadening the API or treating native gradients as a drop-in replacement for deterministic raster gradients.
Goal
Add reliable native PPTX gradient fill support for reusable PptxGenJS surfaces, starting with simple linear gradients on slide backgrounds and shape fills.
This belongs in PptxGenJS when the behavior is generic, schema-valid, and stable across PowerPoint-compatible consumers. Slide-factory should continue using deterministic raster backgrounds when exact rendered appearance is more reliable than native PPTX gradients.
MCP Review Findings
Sources consulted:
ooxmlMCP schema lookups forp:bgPr,a:gradFill,a:gsLst,a:gs,a:lin,a:EG_FillProperties,a:EG_EffectProperties,a:EG_ShadeProperties,a:ST_PositiveFixedPercentage,a:ST_PositiveFixedAngle, anda:ST_TileFlipMode.ooxmlMCP prose sections: ECMA-376 Part 1 sections 19.3.1.2, 20.1.8.33, 20.1.8.36, 20.1.8.37, 20.1.8.41, 20.1.10.43, and 20.1.10.44.microsoft_learnMCP pages for Open XML SDKGradientFillandShapeProperties, plus PowerPoint JavaScript APISlideBackgroundFill,SlideBackgroundGradientFillOptions, andSlideBackgroundGradientFillType.
Confirmed XML constraints for the first slice:
p:bgPrusesCT_BackgroundProperties: exactly one DrawingML fill choice first, then optional DrawingML effects, then optionalp:extLst. Keep emitting<a:effectLst/>after the fill to match the current regression guard and schema order.- Legal
p:bgPrfill choices area:noFill,a:solidFill,a:gradFill,a:blipFill,a:pattFill, anda:grpFill. Image backgrounds should keep usinga:blipFill; this plan only addsa:gradFill. - Presentation shape
p:spPruses the same DrawingML fill group after geometry and beforea:ln. The existing shape fill insertion point is the right one. a:gradFillserializes children in this order: optionala:gsLst, optional shade properties (a:linora:path), then optionala:tileRect. The first implementation should emita:gsLstfollowed bya:linand should omita:pathanda:tileRect.a:gradFillsupports optionalrotWithShapeandflipattributes.flipvalues arenone,x,y, andxy, but it is only relevant oncea:tileRectis supported, so leave it out of the public API initially.a:gsLstcontains two or morea:gschildren. Eacha:gsrequires aposattribute and exactly one DrawingML color choice child.a:gs/@posis anST_PositiveFixedPercentage; public stop positions should be finite0..100percentages and serialize as0..100000.a:lin/@angis anST_PositiveFixedAnglein 60000ths of a degree with legal values from0through less than21600000. Public angles must be normalized into0 <= angle < 360before serialization. Do not allow360to serialize as21600000.a:lin/@scaledis an optional boolean. Emit it only when the public API specifies it, or choose a documented default after comparing PowerPoint fixtures.p:bgPr/@shadeToTitleand the PowerPoint APIShadeFromTitlegradient type are separate from simple linear gradients. Leave shade-to-title support out of the first slice.
Microsoft Learn confirms that PowerPoint exposes background fill types Solid, Gradient, PictureOrTexture, and Pattern, and gradient subtypes Linear, Radial, Rectangular, Path, and ShadeFromTitle. The JavaScript API only exposes a high-level gradient type option for slide backgrounds, so it is useful compatibility signal but not a substitute for inspecting PowerPoint-authored package XML.
PowerPoint Fixture Review Still Outstanding
Before broadening support beyond the first linear-gradient slice, complete the behavior review that MCP lookup cannot replace:
- Compare at least one minimal PowerPoint-authored PPTX fixture for a slide background gradient and one for a shape gradient.
- Confirm PowerPoint's authored defaults for
a:gradFill/@rotWithShape,a:lin/@scaled, gradient stop ordering, and whether it writes two stops or inserts midpoint stops for common presets. - Record only small repo-specific findings and section references. Do not vendor full standards text or large extracted reference material.
Do not expand the public guarantee until the PowerPoint fixture review confirms PowerPoint's authored XML shape and rendering behavior.
Proposed API Slice
Start with a narrow ShapeFillProps extension for linear gradients:
ts
fill: {
type: 'gradient',
gradient: {
kind: 'linear',
angle: 90,
scaled: true,
rotateWithShape: true,
stops: [
{ position: 0, color: '451DC7' },
{ position: 100, color: '0B003D', transparency: 10 },
],
},
}The first slice should avoid path, radial, tile-rectangle, table, chart, and line-gradient promises until each parent surface is independently validated.
The same model should apply directly to slide backgrounds because BackgroundProps already extends ShapeFillProps:
ts
slide.background = {
type: 'gradient',
gradient: {
kind: 'linear',
angle: 90,
stops: [
{ position: 0, color: '451DC7' },
{ position: 100, color: '0B003D' },
],
},
}Initial validation rules:
- Require
type: 'gradient'plusgradient.kind: 'linear'. - Require at least two stops.
- Require finite stop positions in
0..100; reject or warn on invalid input according to the local fill API style chosen during implementation. - Prefer nondecreasing stop positions for deterministic output. If the helper sorts stops, document that behavior in the public API docs and tests.
- Require finite angles, normalize to
0 <= angle < 360, and serialize only legalST_PositiveFixedAnglevalues. - Support
HexColor,ThemeColor, and existing alpha/transparency handling for each stop by routing stop colors throughcreateColorElement().
Implementation Steps
- Extend
ShapeFillPropsinsrc/core-interfaces.tswith a linear gradient model and exported stop type. Expandtypefrom'none' | 'solid'to include'gradient'. - Add a
genXmlGradientFill()helper insrc/gen-utils.ts, called bygenXmlColorSelection()whentype: 'gradient'. KeepgenXmlColorSelectionreturning a complete fill-choice element. - Reuse
createColorElement()for stop colors so hex, theme color, and alpha behavior stays consistent with existing solid fills. - Convert stop positions from public
0..100values to OOXML0..100000values withMath.round(position * 1000). - Normalize gradient angles before calling
convertRotationDegrees(), or hardenconvertRotationDegrees()so360, negative values, and values above one turn cannot produce invalidST_PositiveFixedAnglevalues. - Serialize the first slice as:
xml
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0"><a:srgbClr val="451DC7"/></a:gs>
<a:gs pos="100000"><a:srgbClr val="0B003D"/></a:gs>
</a:gsLst>
<a:lin ang="5400000" scaled="1"/>
</a:gradFill>- Update slide background emission in
src/gen-xml.tsso native background fills are emitted whenslide.backgroundcontains either a solid color or a gradient, while_bkgdImgRidimage backgrounds keep taking precedence. The currentslide.background?.colorcondition is too narrow for gradient backgrounds. - Keep shape fill support on the existing
genXmlColorSelection()path sop:spPrstill emits geometry, then fill, thena:ln. - Avoid using deprecated
background.fillas the gradient API. It currently aliases a legacy color string tobackground.color; directbackground: { type: 'gradient', gradient: ... }is clearer and matchesBackgroundProps extends ShapeFillProps. - Leave chart fills, table fills, and line fills out of the initial public guarantee unless the full review proves their current call sites can support gradient-only fill objects safely.
Fixtures And Tests
Add executable evidence with a small blast radius:
- Add regression tests that inspect
ppt/slides/slide1.xmlfora:gradFillunder a shapep:spPrand under slide backgroundp:bgPr. - Add schema fixtures in
test/schema.test.jsfor a native shape gradient and a native slide background gradient. - Assert that slide background gradients keep
<a:effectLst/>aftera:gradFillinsidep:bgPr. - Assert that shape gradients appear in the fill slot before
a:lninsidep:spPr. - Add invalid-input coverage for fewer than two stops, out-of-range stop positions, and angle normalization around
0,359,360, and negative values. - Add or document minimal PowerPoint-authored comparison fixtures for the same two cases.
- Run:
bash
pnpm run build
pnpm run typecheck
pnpm run test:unit
pnpm run test:schemaIf test:schema cannot run because the validator is missing, install it with ./tools/ooxml-validator/install.sh before treating the implementation as accepted.
Slide-Factory Fallback Guidance
Do not remove raster gradient guidance from slide-factory immediately.
Native gradients can replace deterministic raster backgrounds only after:
- The generated PPTX opens without repair in PowerPoint.
- The gradient survives import or rendering through the target review tools.
- The rendered result is close enough for the specific deck workflow.
- A single native background object remains easier to preserve semantically than an image object.
Until then, slide-factory should continue preferring one deterministic image object over many adjacent solid-color rectangles for continuous gradient backgrounds.