Developer's Guide
This chapter covers the technical aspects of integrating the Focal Point Editor in your TYPO3 templates and custom extensions.
Namespace
The extension uses the namespace:
Nng\Nnfocalpoint
ViewHelpers
The extension provides four ViewHelpers for easy frontend integration.
CSS Styles
The extension includes a frontend CSS file with basic styles. Include it via TypoScript:
page.includeCSS.nnfocalpoint = EXT:nnfocalpoint/Resources/Public/Css/Frontend.css
Or include the static TypoScript "Focal Point Editor" in your template.
Available CSS classes:
.nnfp-image- Automatically added to images fromnnfp:image(width/height 100%, display block).nnfp-container- Container with overflow hidden for focal point images.nnfp-container--16-9,--4-3,--1-1,--3-2,--21-9- Aspect ratio helpers.nnfp-bg- Background image helper (background-size: cover, no-repeat)
Registering the Namespace
The extension registers a global namespace nnfp automatically. You can use the
ViewHelpers without any namespace declaration:
<!-- Global namespace - no declaration needed -->
<nnfp:image image="{image}" width="400" />
<div style="{nnfp:backgroundStyle(image: image)}">
Alternatively, you can use the full namespace declaration:
{namespace nnfocalpoint=Nng\Nnfocalpoint\ViewHelpers}
Or use the XML namespace syntax:
<html xmlns:nnfocalpoint="http://typo3.org/ns/Nng/Nnfocalpoint/ViewHelpers"
data-namespace-typo3-fluid="true">
ImageViewHelper (Drop-in f:image replacement)
A drop-in replacement for f:image that automatically adds focal point styling.
All standard f:image arguments are supported.
Class: Nng\Nnfocalpoint\ViewHelpers\ImageViewHelper
Additional Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
| objectFit | string | No | CSS object-fit value (default: cover) |
| disableFocalPoint | bool | No | Set to true to disable focal point styling |
Usage:
<!-- Simple usage - replaces f:image -->
<nnfp:image image="{image}" width="400" height="300" />
<!-- With crop variant -->
<nnfp:image image="{image}" width="800c" height="600c" cropVariant="mobile" />
<!-- With custom object-fit -->
<nnfp:image image="{image}" width="400" objectFit="contain" />
<!-- Disable focal point (use as regular f:image) -->
<nnfp:image image="{image}" width="400" disableFocalPoint="true" />
Output:
<img src="/fileadmin/_processed_/image.jpg"
width="400" height="300"
class="nnfp-image"
style="object-fit: cover; object-position: 50% 30%;" />
Note
The nnfp-image CSS class is automatically added to all images rendered
with this ViewHelper. This class provides basic styling (width/height 100%,
display block) that works well with container-based layouts.
BackgroundStyleViewHelper
Returns a complete style attribute value for background images with focal point. Use this for divs or other elements with background images.
Class: Nng\Nnfocalpoint\ViewHelpers\BackgroundStyleViewHelper
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
| image | mixed | Yes | The file reference object or array |
| cropVariant | string | No | The crop variant (default: default) |
| width | string | No | Width for image processing (e.g., "1920" or "1920c") |
| height | string | No | Height for image processing |
| backgroundSize | string | No | CSS background-size value (default: cover) |
| includeRepeat | bool | No | Include background-repeat: no-repeat (default: true) |
| fallbackPosition | string | No | Fallback position if no focal point (default: 50% 50%) |
Usage:
<!-- Simple usage -->
<div style="{nnfp:backgroundStyle(image: image)}">
Content here
</div>
<!-- With image processing -->
<div class="hero" style="{nnfp:backgroundStyle(image: image, width: '1920')}">
<h1>Hero Title</h1>
</div>
<!-- With crop variant -->
<div style="{nnfp:backgroundStyle(image: image, cropVariant: 'hero', width: '1920')}">
Content
</div>
<!-- Custom background-size -->
<div style="{nnfp:backgroundStyle(image: image, backgroundSize: 'contain')}">
Content
</div>
Output:
<div style="background-image: url('/fileadmin/image.jpg'); background-size: cover; background-position: 50% 30%; background-repeat: no-repeat">
Content here
</div>
ObjectPositionViewHelper
Returns a CSS object-position value based on the focal point coordinates.
Class: Nng\Nnfocalpoint\ViewHelpers\ObjectPositionViewHelper
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
| image | mixed | Yes | The file reference object or array |
| cropVariant | string | No | The crop variant to get the focal point for (default: default) |
| fallback | string | No | Fallback value if no focal point is set (default: 50% 50%) |
Usage:
<img src="{image.publicUrl}"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image, cropVariant: 'default')}" />
Output:
<img src="/fileadmin/image.jpg"
style="object-fit: cover; object-position: 50% 30%" />
With fallback:
<img src="{image.publicUrl}"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image, fallback: 'center top')}" />
FocalPointViewHelper
Returns the raw focal point data for a specific crop variant.
Class: Nng\Nnfocalpoint\ViewHelpers\FocalPointViewHelper
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
| image | mixed | Yes | The file reference object or array |
| cropVariant | string | No | The crop variant to get the focal point for (default: default) |
| as | string | No | Variable name to assign the focal point to |
Usage (inline):
<f:debug>{nnfp:focalPoint(image: image, cropVariant: 'default')}</f:debug>
Usage (with variable):
<nnfp:focalPoint image="{image}" cropVariant="default" as="fp">
<f:if condition="{fp}">
<p>Focal point: {fp.x} / {fp.y}</p>
<div style="position: absolute; left: {fp.x * 100}%; top: {fp.y * 100}%;"></div>
</f:if>
</nnfp:focalPoint>
Return value:
Returns an array with x and y keys (values 0-1), or null if no focal
point is set:
['x' => 0.5, 'y' => 0.3]
Data Format
Focal points are stored in the sys_file_reference.tx_nnfocalpoint_points field
as a JSON string:
{
"default": {"x": 0.5, "y": 0.3},
"landscape": {"x": 0.9, "y": 0.712},
"portrait": {"x": 0.2, "y": 0.5}
}
Coordinate System:
x: 0= left edgex: 1= right edgey: 0= top edgey: 1= bottom edgex: 0.5, y: 0.5= center of the image
Accessing Focal Points in PHP
From FileReference Object
use TYPO3\CMS\Core\Resource\FileReference;
/** @var FileReference $image */
$focalPointsJson = $image->getProperty('tx_nnfocalpoint_points');
$focalPoints = json_decode($focalPointsJson, true) ?: [];
// Get focal point for a specific variant
$defaultFocalPoint = $focalPoints['default'] ?? null;
if ($defaultFocalPoint) {
$x = $defaultFocalPoint['x'];
$y = $defaultFocalPoint['y'];
}
From Extbase FileReference
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
/** @var FileReference $image */
$originalResource = $image->getOriginalResource();
$focalPointsJson = $originalResource->getProperty('tx_nnfocalpoint_points');
$focalPoints = json_decode($focalPointsJson, true) ?: [];
Custom Integration Examples
The following examples show how to use the focal point data in your templates. The result ensures that the important part of the image remains visible when the container is scaled or cropped.
Using object-position with the focal point coordinates keeps the subject
visible regardless of container dimensions.
Required CSS
For focal points to work correctly, you need CSS that allows the image to be positioned within its container. Here are the essential styles:
/* Basic focal point container */
.focal-container {
position: relative;
overflow: hidden;
}
/* Image with focal point - fills container and respects focal point */
.focal-container img {
width: 100%;
height: 100%;
object-fit: cover;
/* object-position is set inline via ViewHelper */
}
/* Fixed aspect ratio containers */
.focal-container--16-9 {
aspect-ratio: 16 / 9;
}
.focal-container--4-3 {
aspect-ratio: 4 / 3;
}
.focal-container--1-1 {
aspect-ratio: 1 / 1;
}
/* Background image with focal point */
.focal-bg {
background-size: cover;
background-repeat: no-repeat;
/* background-position is set inline via ViewHelper */
}
Example 1: Simple IMG Tag
The most basic usage with a standard <img> tag:
<!-- Basic usage -->
<div class="focal-container" style="width: 300px; height: 200px;">
<img src="{image.publicUrl}"
alt="{image.alternative}"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image)}" />
</div>
<!-- With aspect ratio class -->
<div class="focal-container focal-container--16-9" style="width: 100%; max-width: 800px;">
<img src="{image.publicUrl}"
alt="{image.alternative}"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image, cropVariant: 'default')}" />
</div>
Example 2: Using f:image ViewHelper
Combine TYPO3's f:image ViewHelper with focal point positioning:
<!-- f:image with focal point -->
<div class="focal-container" style="width: 400px; height: 300px;">
<f:image image="{image}"
alt="{image.alternative}"
width="800"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image)}" />
</div>
<!-- f:image with crop variant -->
<div class="focal-container focal-container--4-3">
<f:image image="{image}"
cropVariant="mobile"
width="600c"
height="450c"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image, cropVariant: 'mobile')}" />
</div>
Note
When using f:image with cropping (width="600c"), the focal point provides
additional fine-tuning for how the already-cropped image is positioned in its container.
Example 3: Background Images
Use focal points with CSS background images:
<!-- Simple background image -->
<div class="hero focal-bg"
style="height: 400px;
background-image: url('{image.publicUrl}');
background-position: {nnfp:objectPosition(image: image)};">
<h1>Hero Title</h1>
</div>
<!-- With f:uri.image for processed images -->
<div class="hero focal-bg"
style="height: 500px;
background-image: url('{f:uri.image(image: image, width: 1920)}');
background-position: {nnfp:objectPosition(image: image, cropVariant: 'hero')};">
<div class="hero__content">
<h1>{headline}</h1>
<p>{subheadline}</p>
</div>
</div>
CSS for the hero section:
.hero {
position: relative;
display: flex;
align-items: center;
justify-content: center;
color: white;
text-align: center;
}
.hero::before {
content: '';
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.4);
}
.hero__content {
position: relative;
z-index: 1;
padding: 2rem;
}
Example 4: Custom Logic with FocalPoint ViewHelper
For advanced use cases, access the raw focal point data:
<nnfp:focalPoint image="{image}" cropVariant="default" as="fp">
<f:if condition="{fp}">
<f:then>
<!-- Display focal point coordinates -->
<p class="debug">Focal point: X={fp.x}, Y={fp.y}</p>
<!-- Use in calculations -->
<div class="image-with-hotspot" style="position: relative;">
<img src="{image.publicUrl}" alt="" />
<!-- Hotspot marker at focal point -->
<span class="hotspot"
style="left: {fp.x * 100}%; top: {fp.y * 100}%;">
</span>
</div>
<!-- Conditional positioning based on focal point location -->
<f:if condition="{fp.x} < 0.5">
<f:then>
<div class="text-overlay text-overlay--right">
Text on right (focal point is on left)
</div>
</f:then>
<f:else>
<div class="text-overlay text-overlay--left">
Text on left (focal point is on right)
</div>
</f:else>
</f:if>
</f:then>
<f:else>
<!-- Fallback when no focal point is set -->
<img src="{image.publicUrl}" alt="" style="object-position: center center;" />
</f:else>
</f:if>
</nnfp:focalPoint>
CSS for hotspot marker:
.image-with-hotspot {
position: relative;
display: inline-block;
}
.hotspot {
position: absolute;
width: 20px;
height: 20px;
background: rgba(255, 0, 0, 0.7);
border: 2px solid white;
border-radius: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.2); }
}
Example 5: Responsive Images with Picture Element
Different focal points for different crop variants:
<picture>
<!-- Mobile: square crop with mobile focal point -->
<source media="(max-width: 767px)"
srcset="{f:uri.image(image: image, cropVariant: 'mobile', width: '767c', height: '767c')}" />
<!-- Tablet: 4:3 crop -->
<source media="(max-width: 1023px)"
srcset="{f:uri.image(image: image, cropVariant: 'tablet', width: '1023c', height: '767c')}" />
<!-- Desktop: 16:9 crop -->
<source media="(min-width: 1024px)"
srcset="{f:uri.image(image: image, cropVariant: 'default', width: '1920c', height: '1080c')}" />
<!-- Fallback image -->
<img src="{f:uri.image(image: image, cropVariant: 'default', width: '1920c')}"
alt="{image.alternative}"
class="responsive-hero"
style="object-fit: cover; object-position: {nnfp:objectPosition(image: image, cropVariant: 'default')}" />
</picture>
Example 6: Data Attributes for JavaScript
Pass focal point data to JavaScript for dynamic effects:
<nnfp:focalPoint image="{image}" cropVariant="default" as="fp">
<img src="{image.publicUrl}"
alt="{image.alternative}"
class="js-parallax-image"
data-focal-x="{fp.x}"
data-focal-y="{fp.y}"
data-has-focal-point="{f:if(condition: fp, then: 'true', else: 'false')}" />
</nnfp:focalPoint>
Example JavaScript for parallax effect respecting focal point:
document.querySelectorAll('.js-parallax-image').forEach(img => {
const focalX = parseFloat(img.dataset.focalX) || 0.5;
const focalY = parseFloat(img.dataset.focalY) || 0.5;
window.addEventListener('scroll', () => {
const rect = img.getBoundingClientRect();
const scrollProgress = 1 - (rect.bottom / (window.innerHeight + rect.height));
// Parallax offset based on focal point
const offsetY = (scrollProgress - 0.5) * 50 * (1 - focalY);
img.style.objectPosition = `${focalX * 100}% calc(${focalY * 100}% + ${offsetY}px)`;
});
});
Extending the Extension
Custom Form Element
The focal point form element is registered in ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1702100000] = [
'nodeName' => 'focalPointElement',
'priority' => 40,
'class' => \Nng\Nnfocalpoint\Form\Element\FocalPointElement::class,
];
You can create a custom form element by extending FocalPointElement and
registering it with a higher priority.
Custom ViewHelper
Create custom ViewHelpers by extending the existing ones:
namespace YourVendor\YourExtension\ViewHelpers;
use Nng\Nnfocalpoint\ViewHelpers\ObjectPositionViewHelper;
class CustomObjectPositionViewHelper extends ObjectPositionViewHelper
{
public function render(): string
{
$position = parent::render();
// Add custom logic
return $position;
}
}
API Reference
FocalPointController
Class: Nng\Nnfocalpoint\Controller\FocalPointController
Handles AJAX requests for the backend modal.
Methods:
wizardAction(ServerRequestInterface $request): ResponseInterface- Returns JSON data for the focal point wizard modal, including image URL, dimensions, crop variants, and existing focal points.
imageAction(ServerRequestInterface $request): ResponseInterface- Returns image data for a specific crop variant.
FocalPointElement
Class: Nng\Nnfocalpoint\Form\Element\FocalPointElement
Custom FormEngine element that renders the focal point button and hidden input.
Extends: TYPO3\CMS\Backend\Form\Element\AbstractFormElement
ObjectPositionViewHelper
Class: Nng\Nnfocalpoint\ViewHelpers\ObjectPositionViewHelper
Extends: TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper
Methods:
render(): string- Returns CSS object-position value (e.g., "50% 30%")
FocalPointViewHelper
Class: Nng\Nnfocalpoint\ViewHelpers\FocalPointViewHelper
Extends: TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper
Methods:
render(): mixed- Returns focal point array or renders children with focal point variable