Youngsters Chamara Kapugedera and Prasanna Jayawardene sparked a Sri Lankan batting revival after South Africa threatened to run away with the second Test on Saturday.
The duo put on 105 for the sixth wicket after a four-wicket haul by South African pace spearhead Makhaya Ntini reduced the hosts to 86-5 before lunch on the second day at the Sara Oval.
Veteran Chaminda Vaas (64) and Farveez Maharoof (56) continued the fightback by adding 117 for the eighth wicket as Sri Lanka were bowled out for 321 in reply to South Africa's 361.
The tourists, 40 runs ahead on the first innings, were six for no loss in their second knock at stumps with Herschelle Gibbs on four and Andrew Hall on two.
South Africa, seeking a series-levelling win after being thrashed by an innings and 153 runs in the first Test, lead by 46 runs with all 10 wickets in hand in an absorbing battle between bat and ball.
Seamer Dale Steyn finished with five for 82 and Ntini claimed four for 84 to help South Africa secure the lead.
Kapugedera, a 19-year-old playing his fourth Test, smashed nine boundaries and two sixes in 63 off 77 balls.
Wicket-keeper Jayawardene, who took over the gloves from Kumar Sangakkara in this series, made 42 with six fours and a six in his seventh match.
Both batsmen, however, departed in the space of six deliveries late in the post-lunch session to hand the advantage back to South Africa.
Kapugedera was bowled trying to sweep left-armer Nicky Boje and Jayawardene had his stumps scattered by a fast Steyn delivery.
Vaas and Maharoof pushed the South Africans back with superb strokeplay on both sides of the wicket before Steyn removed both batsmen with the second new ball.
Steyn clean bowled Maharoof, had Vaas caught down the leg-side by wicket-keeper Mark Boucher before forcing last man Muttiah Muralitharan to give a catch at mid-on.
Sri Lanka's top order setbacks contrasted sharply with their display in the first Test where captain Mahela Jayawardene hit the fourth highest score of 374 after sharing a world record stand of 624 with Kumar Sangakkara (287).
Ntini struck in his second over when Upul Tharanga mistimed an attempted hook shot and the ball ballooned to Nicky Boje at mid-on.
Sangakkara, who batted close to 11 hours in the first Test, lasted only 31 deliveries to score 14 when he flicked Ntini to short mid-wicket where Hashim Amla took an overhead catch.
Skipper Jayawardene repeatedly slashed outside the off stump in his brief knock of 13 when another dangerous swish against Steyn found the edge to Boucher.
Ntini returned for his second spell just before lunch to remove Tillekeratne Dilshan and opener Sanath Jayasuriya in the space of three deliveries.
Dilshan was bowled off the inside edge for four and Jayasuriya, who made 47, edged an outswinger to Gibbs at second slip.