Maintain
Customization
Extend NovaSpark with your own CSS, PHP, template overrides and hooks.
Everything here is optional — NovaSpark is fully usable without opening a file. This page is for when you want to go past what the settings screens offer.
Two routes. Custom CSS / JS is update-safe on its own and needs no files. A child theme is for changing templates or adding PHP. Start with the first.
Custom CSS and JS #
At Theme Options → Custom CSS / JS. Three fields:
| Custom CSS | Loaded on every page, after the theme’s own styles — so your rules win without !important. |
| Header Scripts | Injected into <head>. For analytics and verification tags. |
| Footer Scripts | Injected before </body>. Better for anything that does not need to run early. |
Script fields run whatever you paste. Only paste code you understand or got from a service you trust. A broken tag here can stop every page rendering.
Design tokens #
Rather than fixed values, write CSS against NovaSpark’s custom properties. A token follows Theme Options → Design and adapts in dark mode; a hardcoded colour does neither.
.my-banner {
background: rgb(var(--nk-primary));
color: rgb(var(--nk-primary-contrast));
border-radius: var(--nk-radius-brand);
border: 1px solid var(--nk-border-color);
}
| Token | Is |
|---|---|
--nk-primary · --nk-secondary · --nk-accent | Your brand colours. Each has a matching -contrast for readable text on top, and shades from -50 to -950. |
--nk-heading-text · --nk-body-text · --nk-muted-text | Text colours. |
--nk-surface · --nk-surface-raised · --nk-surface-overlay | Background levels. |
--nk-border-color | The standard border colour. |
--nk-radius-brand · --nk-radius-lg · --nk-radius-full | Corner rounding. |
--nk-section-spacing-sm … -xl | Vertical rhythm between sections. |
--nk-container-width · --nk-container-padding | Site width and gutters. |
--nk-font-heading · -body · -secondary · -display · -mono | The five font roles. |
--nk-shadow-sm · -md · -lg | Elevation. |
--nk-transition-fast · -normal · -slow | Animation timings. |
Colours are RGB channels, not colours. That is why they are wrapped in
rgb() — it lets you add transparency with
rgb(var(--nk-primary) / 50%).
Writing for dark mode
Dark mode adds .dark to the <html> element, and tokens change
value underneath. Style with tokens and dark mode works by itself. When you do need something
different:
.dark .my-banner { background: var(--nk-surface-raised); }
Template overrides #
Copy a file from the parent theme into your child theme, keeping the same path, and edit the copy. See Child theme for the files most people change.
Naming and prefixes #
You will see two prefixes in this theme’s code, and they are not interchangeable. Which one you use depends on who owns the thing you are hooking.
| Prefix | Belongs to | Use it when |
|---|---|---|
nk_ |
NovaKit Core — the engine plugin | Hooking the engine: actions, filters, post types, meta keys. Works in any NovaKit-powered theme. |
novaspark_ |
NovaSpark — this theme | Calling or replacing one of the theme’s own template functions. |
The split exists because NovaKit is a separate product. It powers this theme, but it is designed to power others too — so anything a second theme would also need carries the engine’s prefix, and anything specific to NovaSpark’s markup carries the theme’s.
A rule of thumb. If you are hooking something you’d expect
to exist in a different NovaKit theme — the header slot, a Global Block
type, the widget list — it is nk_. If it only makes sense
because of how this theme draws a page, it is novaspark_.
// Engine hook — the same code works in any NovaKit theme.
add_action( 'nk_before_header', function (): void {
echo '<div class="my-topbar">Free shipping this week</div>';
} );
// Theme function — specific to how NovaSpark renders a post.
if ( function_exists( 'novaspark_reading_time' ) ) {
// Returns an int — minutes.
echo esc_html( novaspark_reading_time() . ' min read' );
}
Never rename a nk_ identifier in the database.
Post types like nk_global_section, meta keys like
_nk_mm_enabled and the nk_ option keys are the
storage layer. Changing them does not rename your content — it hides it,
because the code goes looking under the old name and finds nothing.
CSS classes and data- attributes are a separate layer again and
use the ns- prefix (.nk-header,
data-nk-theme). Those are presentation only — safe to target
from your own stylesheet, and never a place to store anything.
Hooks and filters #
Every hook below is an engine hook, so the same snippet works in any
NovaKit-powered theme. Put them in your child theme’s
functions.php.
Layout actions
Fixed points in the page where you can print your own markup. None of them take arguments.
| Action | Fires |
|---|---|
nk_before_header | Before the header. Announcement bars, overlays. |
nk_header | Where the header itself is drawn. |
nk_after_header | Just after the header. |
nk_page_cover | Where the page cover is drawn. |
nk_before_footer | Before the footer. |
nk_footer | Where the footer is drawn. |
nk_after_footer | At the very end of the page. |
// nk_before_header — an announcement bar above everything.
add_action( 'nk_before_header', function (): void {
echo '<div class="my-topbar">Free shipping this week</div>';
} );
// nk_header — priority 5 runs before NovaKit draws its own header.
add_action( 'nk_header', 'my_custom_header', 5 );
// nk_after_header — the theme's own breadcrumbs, which echo directly.
add_action( 'nk_after_header', 'novaspark_breadcrumbs' );
// nk_page_cover — swap in your own cover for one post type.
add_action( 'nk_page_cover', function (): void {
if ( is_singular( 'product' ) ) {
get_template_part( 'template-parts/product-cover' );
}
} );
// nk_before_footer — a call-to-action band above the footer.
add_action( 'nk_before_footer', function (): void {
echo do_shortcode( '[my_newsletter_cta]' );
} );
// nk_footer — an extra row inside the footer, after NovaKit's.
add_action( 'nk_footer', 'my_extra_footer_row', 20 );
// nk_after_footer — the very last thing on the page.
add_action( 'nk_after_footer', function (): void {
echo '<div id="my-cookie-bar"></div>';
} );
Event actions
These fire when something happens rather than at a point in the page, and
most of them pass arguments — remember the 10, 2 on
add_action() when you need the second one.
| Action | Arguments | Fires |
|---|---|---|
nk_settings_updated | — | After Theme Options are saved or reset. |
nk_account_registered | int $user_id | After someone registers through a NovaKit Auth form. |
nk_form_custom_submit | array $values, string $handle | When a custom form is submitted. |
nk_render_global_section | int $section_id, string $slot | As a Global Block is drawn. $slot is header or footer. |
// Theme Options were saved — clear anything you derived from them.
add_action( 'nk_settings_updated', function (): void {
delete_transient( 'my_nav_cache' );
} );
add_action( 'nk_account_registered', function ( int $user_id ): void {
$user = get_userdata( $user_id );
wp_mail(
$user->user_email,
'Welcome aboard',
'Your account is ready.'
);
} );
add_action( 'nk_form_custom_submit', function ( array $values, string $handle ): void {
if ( 'contact' !== $handle ) {
return;
}
my_push_to_crm( $values[ 'email' ] ?? '' );
}, 10, 2 );
add_action( 'nk_render_global_section', function ( int $section_id, string $slot ): void {
if ( 'footer' === $slot ) {
my_track_block( $section_id );
}
}, 10, 2 );
Filters
Each one receives a value and must return it — forgetting
the return is the most common way to blank out a list by accident.
| Filter | Changes |
|---|---|
nk_elementor_widget_enabled | Whether a widget is registered. Return false to remove one. |
nk_section_types | The list of Global Block types. |
nk_design_presets | The design presets offered in Theme Options. |
nk_dashboard_recommended_plugins | The plugin list on the dashboard. |
nk_demo_sources | Where demos are fetched from. |
nk_library_sources | Where NovaKit Library templates come from. |
nk_smart_loops_card_styles | The card styles Smart Loops offers. |
nk_testimonial_skins | The testimonial skins available. |
nk_testimonial_containers | How testimonials are arranged (grid, masonry, carousel, marquee). |
nk_icon_sets_to_load | Which icon sets load on the front end. |
nk_custom_header_args | The custom-header theme support arguments. |
nk_archive_card_template_id | Which Card Global Block renders a given post. |
add_filter( 'nk_elementor_widget_enabled', function ( bool $enabled, string $name ): bool {
// Widget names are the get_name() value, e.g. 'nk-brands'.
return 'nk-brands' === $name ? false : $enabled;
}, 10, 2 );
add_filter( 'nk_section_types', function ( array $types ): array {
// slug => label. Adds a new type to the Global Blocks taxonomy.
$types[ 'promo' ] = __( 'Promo Bar', 'my-child' );
return $types;
} );
add_filter( 'nk_design_presets', function ( array $presets ): array {
// Hide a bundled preset from Theme Options.
unset( $presets[ 'finance' ] );
return $presets;
} );
add_filter( 'nk_dashboard_recommended_plugins', function ( array $plugins ): array {
return array_values( array_filter(
$plugins,
fn( array $p ): bool => 'jetpack' !== $p[ 'slug' ]
) );
} );
add_filter( 'nk_demo_sources', function ( array $sources ): array {
// Must implement DemoImporter\Sources\DemoSourceInterface.
$sources[] = new My_Demo_Source();
return $sources;
} );
add_filter( 'nk_library_sources', function ( array $sources ): array {
// Must implement Library\Sources\SourceInterface.
$sources[] = new My_Library_Source();
return $sources;
} );
add_filter( 'nk_smart_loops_card_styles', function ( array $styles ): array {
// slug => [ 'label' => …, 'render' => callable ]
unset( $styles[ 'custom-template' ] );
return $styles;
} );
add_filter( 'nk_testimonial_skins', function ( array $skins ): array {
unset( $skins[ 'custom-template' ] );
return $skins;
} );
add_filter( 'nk_testimonial_containers', function ( array $containers ): array {
// slug => [ 'label' => …, 'engine' => … ]
unset( $containers[ 'marquee' ] );
return $containers;
} );
add_filter( 'nk_icon_sets_to_load', function ( array $used, array $enabled ): array {
// $used is what NovaKit detected on the page; $enabled is every set
// switched on. Return $enabled to stop relying on detection.
return $enabled;
}, 10, 2 );
add_filter( 'nk_custom_header_args', function ( array $args ): array {
$args[ 'width' ] = 2560;
$args[ 'height' ] = 600;
return $args;
} );
add_filter( 'nk_archive_card_template_id', function ( int $id, WP_Post $post ): int {
// Use a different Card Global Block for one post type.
return 'product' === $post->post_type ? 1234 : $id;
}, 10, 2 );
Alpine and GSAP in brief #
NovaSpark uses two small libraries on the front end, with a clean split: Alpine.js handles interactive state — what is open, which tab is active — and GSAP handles movement.
Both are already loaded, so a child theme can use them without adding anything. Alpine
components are attached with x-data in the markup; NovaSpark registers its own on
alpine:init, so register yours the same way if you add any.
You will not usually need either. Motion is available through NovaKit Motion without code, and the interactive widgets bring their own behaviour. These are here for the cases the settings do not cover.