# How i Implemented a Flexible Dimension Control in Open Quran View v0.5.0

> How we solved the aspect ratio constraint problem and created a more flexible rendering system for Quran pages.

* * *

![](https://cdn.hashnode.com/uploads/covers/624c1b304b4dde0dc0bec7d4/11d09f4a-e0de-4777-b8d3-0a57a4c90ddf.png align="center")

Live Demo: http://mubin.adelpro.us.kg

## The Problem with Fixed Ratios

When Open Quran View first launched, it used a fixed 0.7 mushaf ratio (width/height) to derive missing dimensions. This worked perfectly when your container matched the standard mushaf proportions:

```tsx

// Default behavior - derives height from width using 0.7 ratio

<OpenQuranView width={600} />

// Results in: width=600, height=857 (600 / 0.7)

```

But real-world applications have varied container sizes. A mobile app might have a short, wide container. A website might have a tall, narrow sidebar. A fullscreen viewer might want maximum width at the expense of height.

The old system couldn't handle these cases elegantly:

| Container Shape | Old Behavior |

|-----------------|--------------|

| Wider than 0.7 ratio | Text overflowed horizontally |

| Taller than 0.7 ratio | Wasted space, text too small |

| Neither dim provided | Only width tracked, height derived |

### The Font Size Problem

The layout calculator derived font size from height:

```typescript

const computedLineHeight = availableHeight / 15;

const fontSize = lineHeight / 1.5;

```

This meant text width depended solely on `chars * fontSize * 0.5`. When height was artificially large (like a tall, narrow container), the font grew accordingly—but the line width didn't, causing words to overflow and overlap.

* * *

## The Solution: Two New Props

### 1\. The `ratio` Prop

Control whether dimension derivation happens at all:

```tsx

// Default - use mushaf ratio (0.7) to derive missing dimension

<OpenQuranView ratio={true} />

// Disable derivation - fill container exactly

<OpenQuranView ratio={false} />

// Custom ratio value

<OpenQuranView ratio={0.8} />

```

| Value | Behavior |

|-------|----------|

| `true` (default) | Uses 0.7 mushaf ratio to derive missing dimension |

| `false` | No derivation; uses explicit dims or container measurements |

| `number` | Custom ratio (e.g., `0.8`, `1.0`) overrides default |

### 2\. The `fit` Prop

Control which dimension fills the container when neither is provided:

```tsx

// Default - fill width, derive height

<OpenQuranView fit="width" />

// Fill height, derive width

<OpenQuranView fit="height" />

```

| Value | Behavior |

|-------|----------|

| `"width"` (default) | ResizeObserver tracks container width; height derived |

| `"height"` | ResizeObserver tracks container height; width derived |

* * *

## Real-World Use Cases

### Fullscreen Wide Viewing

```tsx

// User wants maximum width for immersive reading

<OpenQuranView

fit="width"

ratio={true}

className="fullscreen-viewer"

/>

```

The component fills the screen width, and height follows naturally. User scrolls vertically to read.

### Horizontal Scroll for Wide Screens

```tsx

// Dashboard with wide container - scroll sideways

<OpenQuranView

fit="height"

ratio={true}

style={{ height: '100vh' }}

/>

```

The component fills the full height, and width expands accordingly. User scrolls horizontally.

### Fixed-Size Widget

```tsx

// Fixed dimensions - no derivation

<OpenQuranView

width={300}

height={400}

ratio={false}

/>

```

The component uses exactly 300x400, regardless of mushaf proportions.

* * *

## The Font Size Fix

### The Core Issue

When width and height don't match the mushaf ratio, font calculation broke down:

```plaintext

Container: 800px wide × 600px tall

Ratio: 1.33 (width/height)

Old calculation:

- lineHeight = 600 / 15 = 40px
- fontSize = 40 / 1.5 = 26.7px
- Line width = ~600px (text fits)

But derived height = 800 / 0.7 = 1143px

Font becomes too large for the actual container!

```

### The min() Solution

We now calculate font size from both constraints and use the minimum:

```typescript

// Calculate font size from height (original approach)

const fontSizeFromHeight = lineHeight / 1.5;

// Calculate font size from width constraint

// ~80 chars per line is a safe estimate for Arabic text

const availableWidth = pageWidth - paddingLeft - paddingRight;

const avgCharsPerLine = 80;

const fontSizeFromWidth = availableWidth / (avgCharsPerLine * 0.5);

// Use the minimum to ensure text fits both constraints

const fontSize = Math.min(fontSizeFromHeight, fontSizeFromWidth);

```

This ensures:

*   Text never overflows horizontally
    
*   Font is as large as possible within both constraints
    
*   The 15-line Al-Madinah Mushaf standard is preserved
    

### Mathematical Proof

For a container `W × H`:

```plaintext

fontSizeFromHeight = (H × 0.9) / 15 / 1.5 = H / 25

fontSizeFromWidth = W / 40  (assuming 80 chars, 0.5 multiplier)

Text fits when: fontSize ≤ fontSizeFromWidth

fontSize ≤ fontSizeFromHeight

Therefore: fontSize ≤ min(W / 40, H / 25)

```

* * *

## Prop Validation

To help developers avoid confusion, the component emits development-only warnings when props conflict:

```tsx

// Both dimensions + fit - fit is ignored

<OpenQuranView width={600} height={850} fit="height" />

// Warning: "Both width and height are provided. The fit prop will be ignored."

// fit="height" with explicit width - fit is ignored

<OpenQuranView width={600} fit="height" />

// Warning: "fit="height" is ignored when width is provided without height"

// ratio=false with fit - fit is ignored

<OpenQuranView ratio={false} fit="height" />

// Warning: "fit is ignored when ratio={false}"

// Fullscreen with explicit dims - dims ignored

<OpenQuranView fullscreen width={600} />

// Warning: "Explicit width/height are ignored in fullscreen mode"

```

These warnings only appear in development (`process.env.NODE_ENV !== 'production'`) and are stripped from production bundles.

* * *

## Migration Guide

### v0.4.x → v0.5.0

*   ***No breaking changes.***\* The new props have safe defaults that match v0.4.x behavior.
    

```tsx

// Before (v0.4.x)

<OpenQuranView width={600} />

// After (v0.5.0) - identical behavior

<OpenQuranView width={600} />

```

### New Capabilities

```tsx

// Disable ratio derivation entirely

<OpenQuranView ratio={false} />

// Use custom ratio

<OpenQuranView ratio={0.75} />

// Fill height instead of width

<OpenQuranView fit="height" />

```

### Behavior Comparison

| Props | v0.4.x | v0.5.0 |

|-------|--------|--------|

| `width={600}` | width=600, height derived | Same |

| `height={800}` | height=800, width derived | Same |

| `ratio={false}` | N/A | No derivation |

| `fit="height"` | N/A | Fill height instead |

| `ratio={0.8}` | N/A | Custom ratio |

* * *

## Under the Hood

### ResizeObserver Changes

The first `useEffect` now tracks dimensions based on `fit`:

```typescript

const resizeObserver = new ResizeObserver((entries) => {

for (const entry of entries) {

const rect = entry.contentRect;

if (fit === "height") {

if (rect.height > 0) setContainerHeight(rect.height);

} else {

if (rect.width > 0) setContainerWidth(rect.width);

}

}

});

```

### Derivation Logic

The second `useEffect` handles dimension derivation:

```typescript

const actualRatio = typeof ratio === "number" ? ratio : MUSHAF_RATIO;

if (height && !isFullscreen) {

const derivedWidth = height * actualRatio;

setContainerWidth(derivedWidth);

setContainerHeight(height);

return;

}

if (containerWidth > 0) {

setContainerHeight(containerWidth / actualRatio);

}

```

When `ratio={false}`, this entire effect is skipped.

### Layout Calculator Integration

The `createLayoutCalculator` function now uses the min approach:

```typescript

const fontSizeFromHeight = lineHeight / 1.5;

const fontSizeFromWidth = availableWidth / (avgCharsPerLine * 0.5);

const fontSize = options.fontSize || Math.min(fontSizeFromHeight, fontSizeFromWidth);

```

* * *

## API Reference

### React Component

```tsx

import { OpenQuranView } from '@open-quran-view/view';

<OpenQuranView

page={1}

width={600}

height={850}

ratio={true}

fit="width"

theme="light"

mushafLayout="hafs-v2"

onPageChange={(page) => console.log(page)}

onWordClick={(word) => console.log(word)}

/>

```

## Web Component

```html

<open-quran-view

page="1"

width="600"

height="850"

ratio="true"

fit="width"

theme="light"

mushaf-layout="hafs-v2"

></open-quran-view>

```

### Prop Details

| Prop | Type | Default | Description |

|------|------|---------|-------------|

| `ratio` | `boolean \| number` | `true` | Aspect ratio control |

| `fit` | `"width" \| "height"` | `"width"` | Which dim to fill |

* * *

## Performance Considerations

The new props don't add runtime overhead:

*   `ratio` and `fit` are just conditionals in existing effects
    
*   Font calculation still happens once per page render
    
*   ResizeObserver usage remains the same
    

Minification strips dev warnings from production:

```javascript

// Development

if (process.env.NODE_ENV !== "production") {

console.warn("...");

}

// Production (UglifyJS/Terser drops unreachable code)

if (false) {

console.warn("...");

}

```

Result: zero warnings in production, full guidance in development.

* * *

## Conclusion

Open Quran View v0.5.0 brings flexible dimension control without breaking existing behavior. The new `ratio` and `fit` props let developers create diverse layouts while the font size fix ensures text never overflows.

The key insight: **constraints should inform, not limit**. By calculating font size from both width and height constraints, we created a system that adapts to any container shape while maintaining the beautiful mushaf typography.

Try it out:

```bash

npm install open-quran-view@latest

```

* * *

*   *Jazakum Allahu Khairan for reading. May this serve the Muslim ummah in accessing the Quran beautifully on any device.*\*
